From be8dbf9fb7b5d2f5a3a1ac43e302ae15f57db894 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Tue, 14 Mar 2023 23:15:50 +0530 Subject: [PATCH 01/25] Refactor JS procedure --- procedures_js/alert_processor.js | 135 ++++++++++++++++++------------- 1 file changed, 78 insertions(+), 57 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index e93dfe8..fe5c51c 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -1,7 +1,6 @@ -// args -var CORRELATION_PERIOD_MINUTES +const CORRELATION_PERIOD_MINUTES = -60 -CORRELATION_PERIOD_MINUTES = CORRELATION_PERIOD_MINUTES || 60 +var alert_correlation_result = [] // library function exec(sqlText, binds = []) { @@ -24,60 +23,82 @@ function exec(sqlText, binds = []) { return retval } -CORRELATE = ` -MERGE INTO ${results_alerts_table} dst -USING ( - SELECT - d.id alert_id_to_update, - COALESCE( - p.correlation_id, - d.correlation_id, - UUID_STRING() - ) correlation_id - FROM ${data_alerts_view} d -- destination - LEFT OUTER JOIN ${data_alerts_view} p -- potential chain - ON ( - d.id != p.id - AND ( - d.alert_time > p.alert_time - OR ( - d.alert_time = p.alert_time - AND d.id > p.id - ) - ) - AND p.alert_time > d.alert_time - INTERVAL '1 hour' - AND p.correlation_id IS NOT NULL - AND p.actor = d.actor - AND ( - p.object = d.object - OR p.action = d.action - ) - ) - WHERE d.suppressed = FALSE - AND d.alert_time > CURRENT_TIMESTAMP - INTERVAL '$${CORRELATION_PERIOD_MINUTES} minutes' - QUALIFY 1=ROW_NUMBER() OVER ( - PARTITION BY d.id -- one update per destination id - ORDER BY -- most recent wins - p.alert_time DESC, p.id DESC - ) -) src -ON ( - dst.alert:ALERT_ID = src.alert_id_to_update - AND ( - dst.correlation_id IS NULL - OR dst.correlation_id != src.correlation_id - ) -) -WHEN MATCHED THEN UPDATE SET - correlation_id = src.correlation_id +GET_CORRELATED_ALERT = ` +SELECT * +FROM results.alerts +WHERE alert:ACTOR = ? + AND (alert:OBJECT::STRING = ? OR alert:ACTION::STRING = ?) + AND correlation_id IS NOT NULL + AND NOT IS_NULL_VALUE(alert:ACTOR) + AND suppressed = FALSE + AND event_time > DATEADD(minutes, $${CORRELATION_PERIOD_MINUTES}, ?) +ORDER BY event_time DESC +LIMIT 1 ` -var n, - results = [] -do { - n = exec(CORRELATE)[0]['number of rows updated'] - results.push(n) -} while (n != 0) -return { - ROWS_UPDATED: results, +function generate_uuid() { + GENERATE_UUID = `SELECT UUID_STRING()` + return exec(GENERATE_UUID)[0][['UUID_STRING()']] +} + +function get_correlation_id(alert) { + if ( + alert['ACTOR'] == undefined || + alert['OBJECT'] == undefined || + alert['ACTION'] == undefined || + alert['EVENT_TIME'] == undefined + ) { + return generate_uuid() + } else { + actor = alert['ACTOR'] + object = alert['OBJECT'] + action = alert['ACTION'] + time = alert['EVENT_TIME'] + + if (object instanceof Array) { + o = '","'.join(object) + object = `'["$${o}"]'` + } + if (action instanceof Array) { + o = '","'.join(action) + object = `'["$${o}"]'` + } + } + + try { + match = exec(GET_CORRELATED_ALERT, [actor, object, action, time]) + } catch (e) { + match = [] + } + correlation_id = + match.length > 0 && 'CORRELATION_ID' in match[0] + ? match[0]['CORRELATION_ID'] + : generate_uuid() + + return correlation_id +} + +GET_ALERTS_WITHOUT_CORREALTION_ID = `SELECT * +FROM results.alerts +WHERE correlation_id IS NULL + AND suppressed = FALSE + AND alert_time > DATEADD(hour, -2, CURRENT_TIMESTAMP())` + +UNCORRELATED_ALERTS = exec(GET_ALERTS_WITHOUT_CORREALTION_ID) + +UPDATE_ALERT_CORRELATION_ID = ` +UPDATE results.alerts +SET correlation_id=? +WHERE alert:EVENT_TIME > DATEADD(minutes, $${CORRELATION_PERIOD_MINUTES}, ?) + AND alert:ALERT_ID=? +` +for (const x of UNCORRELATED_ALERTS) { + alert_body = x['ALERT'] + alert_id = alert_body['ALERT_ID'] + correlation_id = get_correlation_id(alert_body) + event_time = alert_body['EVENT_TIME'] + alert_correlation_result.push( + exec(UPDATE_ALERT_CORRELATION_ID, [correlation_id, event_time, alert_id]) + ) } +return { alert_correlation_result: alert_correlation_result } From dcfa8c26e84ddb982926b95129f46967e627d5b5 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Thu, 16 Mar 2023 16:52:12 +0530 Subject: [PATCH 02/25] Update alert_processor.js --- procedures_js/alert_processor.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index fe5c51c..cd10d8d 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -43,10 +43,10 @@ function generate_uuid() { function get_correlation_id(alert) { if ( - alert['ACTOR'] == undefined || - alert['OBJECT'] == undefined || - alert['ACTION'] == undefined || - alert['EVENT_TIME'] == undefined + 'ACTOR' in alert == false || + 'OBJECT' in alert == false || + 'ACTION' in alert == false || + 'EVENT_TIME' in alert == false ) { return generate_uuid() } else { @@ -56,11 +56,11 @@ function get_correlation_id(alert) { time = alert['EVENT_TIME'] if (object instanceof Array) { - o = '","'.join(object) + o = object.join('","') object = `'["$${o}"]'` } if (action instanceof Array) { - o = '","'.join(action) + o = object.join('","') object = `'["$${o}"]'` } } From 3994fe8b32bbda4cb69927c474d35a4085956ce0 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Thu, 16 Mar 2023 17:45:27 +0530 Subject: [PATCH 03/25] Update alert_processor.js --- procedures_js/alert_processor.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index cd10d8d..3774302 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -84,19 +84,19 @@ WHERE correlation_id IS NULL AND suppressed = FALSE AND alert_time > DATEADD(hour, -2, CURRENT_TIMESTAMP())` -UNCORRELATED_ALERTS = exec(GET_ALERTS_WITHOUT_CORREALTION_ID) - UPDATE_ALERT_CORRELATION_ID = ` UPDATE results.alerts -SET correlation_id=? +SET correlation_id = ? WHERE alert:EVENT_TIME > DATEADD(minutes, $${CORRELATION_PERIOD_MINUTES}, ?) - AND alert:ALERT_ID=? + AND alert:ALERT_ID = ? ` +UNCORRELATED_ALERTS = exec(GET_ALERTS_WITHOUT_CORREALTION_ID) + for (const x of UNCORRELATED_ALERTS) { - alert_body = x['ALERT'] + alert_body = x alert_id = alert_body['ALERT_ID'] correlation_id = get_correlation_id(alert_body) - event_time = alert_body['EVENT_TIME'] + event_time = String(alert_body['EVENT_TIME']) alert_correlation_result.push( exec(UPDATE_ALERT_CORRELATION_ID, [correlation_id, event_time, alert_id]) ) From 9825453e68732964d67c6ab9c4c449624ea8c279 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Fri, 17 Mar 2023 17:07:20 +0530 Subject: [PATCH 04/25] Update alert_processor.js --- procedures_js/alert_processor.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index 3774302..f0027b9 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -90,15 +90,18 @@ SET correlation_id = ? WHERE alert:EVENT_TIME > DATEADD(minutes, $${CORRELATION_PERIOD_MINUTES}, ?) AND alert:ALERT_ID = ? ` + UNCORRELATED_ALERTS = exec(GET_ALERTS_WITHOUT_CORREALTION_ID) for (const x of UNCORRELATED_ALERTS) { - alert_body = x - alert_id = alert_body['ALERT_ID'] + alert_body = x['ALERT'] correlation_id = get_correlation_id(alert_body) event_time = String(alert_body['EVENT_TIME']) + alert_id = alert_body['ALERT_ID'] + alert_correlation_result.push( exec(UPDATE_ALERT_CORRELATION_ID, [correlation_id, event_time, alert_id]) ) } + return { alert_correlation_result: alert_correlation_result } From 6011087db39496cf3db1718d3e7a1c82914337e0 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Fri, 17 Mar 2023 17:26:45 +0530 Subject: [PATCH 05/25] Remove data.alerts template param from processor --- procedures.tf | 5 ----- procedures_js/alert_processor.js | 9 +++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/procedures.tf b/procedures.tf index 1d4cf96..d408e71 100644 --- a/procedures.tf +++ b/procedures.tf @@ -83,11 +83,6 @@ resource "snowflake_procedure" "alert_processor" { local.results_schema, local.alerts_table, ]) - data_alerts_view = join(".", [ - local.snowalert_database_name, - local.data_schema, - snowflake_view.alerts.name, - ]) }) depends_on = [ diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index f0027b9..def4f16 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -25,7 +25,7 @@ function exec(sqlText, binds = []) { GET_CORRELATED_ALERT = ` SELECT * -FROM results.alerts +FROM ${results_alerts_table} WHERE alert:ACTOR = ? AND (alert:OBJECT::STRING = ? OR alert:ACTION::STRING = ?) AND correlation_id IS NOT NULL @@ -78,14 +78,15 @@ function get_correlation_id(alert) { return correlation_id } -GET_ALERTS_WITHOUT_CORREALTION_ID = `SELECT * -FROM results.alerts +GET_ALERTS_WITHOUT_CORREALTION_ID = ` +SELECT * +FROM ${results_alerts_table} WHERE correlation_id IS NULL AND suppressed = FALSE AND alert_time > DATEADD(hour, -2, CURRENT_TIMESTAMP())` UPDATE_ALERT_CORRELATION_ID = ` -UPDATE results.alerts +UPDATE ${results_alerts_table} SET correlation_id = ? WHERE alert:EVENT_TIME > DATEADD(minutes, $${CORRELATION_PERIOD_MINUTES}, ?) AND alert:ALERT_ID = ? From 3854f453af3c26ea65b681b64a209fd35a66e05a Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Mon, 20 Mar 2023 21:20:50 +0530 Subject: [PATCH 06/25] Formatting --- procedures_js/alert_processor.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index def4f16..e6eab1f 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -83,7 +83,8 @@ SELECT * FROM ${results_alerts_table} WHERE correlation_id IS NULL AND suppressed = FALSE - AND alert_time > DATEADD(hour, -2, CURRENT_TIMESTAMP())` + AND alert_time > DATEADD(hour, -2, CURRENT_TIMESTAMP()) +` UPDATE_ALERT_CORRELATION_ID = ` UPDATE ${results_alerts_table} From 11179a083e6a5ef4ff30f45db507b34f0ac75bd0 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Fri, 24 Mar 2023 13:47:53 +0530 Subject: [PATCH 07/25] Add CORRELATION_PERIOD_MINUTES arg --- procedures_js/alert_processor.js | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index e6eab1f..c1c7ab3 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -1,4 +1,7 @@ -const CORRELATION_PERIOD_MINUTES = -60 +//args +var CORRELATION_PERIOD_MINUTES + +CORRELATION_PERIOD_MINUTES = CORRELATION_PERIOD_MINUTES || -60 var alert_correlation_result = [] @@ -49,30 +52,27 @@ function get_correlation_id(alert) { 'EVENT_TIME' in alert == false ) { return generate_uuid() - } else { - actor = alert['ACTOR'] - object = alert['OBJECT'] - action = alert['ACTION'] - time = alert['EVENT_TIME'] - - if (object instanceof Array) { - o = object.join('","') - object = `'["$${o}"]'` - } - if (action instanceof Array) { - o = object.join('","') - object = `'["$${o}"]'` - } } - try { - match = exec(GET_CORRELATED_ALERT, [actor, object, action, time]) - } catch (e) { - match = [] + actor = alert['ACTOR'] + object = alert['OBJECT'] + action = alert['ACTION'] + time = alert['EVENT_TIME'] + + if (object instanceof Array) { + o = object.join('","') + object = `'["$${o}"]'` } + if (action instanceof Array) { + o = action.join('","') + action = `'["$${o}"]'` + } + + match = exec(GET_CORRELATED_ALERT, [actor, object, action, time]) + correlation_id = - match.length > 0 && 'CORRELATION_ID' in match[0] - ? match[0]['CORRELATION_ID'] + match.length > 0 && 'CORRELATION_ID' in match + ? match['CORRELATION_ID'] : generate_uuid() return correlation_id From 1dce9355c5bc7e661b4400da84a28c3933970ef8 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Fri, 24 Mar 2023 17:39:28 +0530 Subject: [PATCH 08/25] Update alert_processor.js --- procedures_js/alert_processor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index c1c7ab3..bece1ed 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -68,7 +68,7 @@ function get_correlation_id(alert) { action = `'["$${o}"]'` } - match = exec(GET_CORRELATED_ALERT, [actor, object, action, time]) + match = exec(GET_CORRELATED_ALERT, [actor, object, action, time])[0] correlation_id = match.length > 0 && 'CORRELATION_ID' in match From 334e7291b007c252d0073cfa9381906deb6e5901 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Fri, 24 Mar 2023 17:42:37 +0530 Subject: [PATCH 09/25] Update alert_processor.js --- procedures_js/alert_processor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index bece1ed..07bfdcd 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -71,7 +71,7 @@ function get_correlation_id(alert) { match = exec(GET_CORRELATED_ALERT, [actor, object, action, time])[0] correlation_id = - match.length > 0 && 'CORRELATION_ID' in match + match && 'CORRELATION_ID' in match ? match['CORRELATION_ID'] : generate_uuid() From 6dc2de39f2b90f8de7a19c567b2a3befd7434ebb Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Fri, 24 Mar 2023 19:09:42 +0530 Subject: [PATCH 10/25] Update alert_processor.js --- procedures_js/alert_processor.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index 07bfdcd..f6869f0 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -1,8 +1,6 @@ //args var CORRELATION_PERIOD_MINUTES -CORRELATION_PERIOD_MINUTES = CORRELATION_PERIOD_MINUTES || -60 - var alert_correlation_result = [] // library From 4e7afd865a09e89afe29dc451a98c59fc99a2123 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Fri, 24 Mar 2023 20:18:15 +0530 Subject: [PATCH 11/25] Update procedures.tf --- procedures.tf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/procedures.tf b/procedures.tf index d408e71..fad56d9 100644 --- a/procedures.tf +++ b/procedures.tf @@ -75,6 +75,11 @@ resource "snowflake_procedure" "alert_processor" { name = "ALERT_PROCESSOR" language = "JAVASCRIPT" + arguments { + name = "correlation_period_minutes" + type = "NUMBER" + } + return_type = "VARIANT" execute_as = "CALLER" statement = templatefile("${path.module}/procedures_js/alert_processor.js", { From 9b6b4fd85ed1b655a5d9456c5eb66b9350179590 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Fri, 24 Mar 2023 21:50:47 +0530 Subject: [PATCH 12/25] Add task and schedule for the alert processor --- examples/complete/main.tf | 1 + .../complete/snowalert.auto.tfvars.sample | 1 + examples/complete/variables.tf | 6 ++++++ examples/simple/snowalert.auto.tfvars.sample | 1 + examples/simple/variables.tf | 6 ++++++ procedures.tf | 2 +- tasks.tf | 19 +++++++++++++++++++ variables.tf | 6 ++++++ 8 files changed, 41 insertions(+), 1 deletion(-) diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 6940ee5..afc5738 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -21,6 +21,7 @@ module "snowalert" { snowalert_database_name = var.snowalert_database_name alerts_merge_schedule = var.alerts_merge_schedule + alert_processor_schedule = var.alert_processor_schedule alert_dispatch_schedule = var.alert_dispatch_schedule alert_scheduler_schedule = var.alert_scheduler_schedule diff --git a/examples/complete/snowalert.auto.tfvars.sample b/examples/complete/snowalert.auto.tfvars.sample index 20d7d33..ae5e9c3 100644 --- a/examples/complete/snowalert.auto.tfvars.sample +++ b/examples/complete/snowalert.auto.tfvars.sample @@ -10,6 +10,7 @@ snowalert_warehouse_name = "SNOWALERT_WAREHOUSE" snowalert_database_name = "SNOWALERT" alerts_merge_schedule = "0 12 * * *" # daily at 12 pm: https://crontab.guru/#0_12_*_*_* +alert_processor_schedule = "0 * * * * " # every hour: https://crontab.guru/#0_*_*_*_* alert_dispatch_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_scheduler_schedule = "1/15 * * * *" # every 15 mins: https://crontab.guru/#1/15_*_*_*_* diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index bdc8bbc..38e7d48 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -162,6 +162,12 @@ variable "alerts_merge_schedule" { default = "0 12 * * *" } +variable "alert_processor_schedule" { + type = string + description = "Schedule for the alert processor task." + default = "0 * * * *" +} + variable "alert_dispatch_schedule" { type = string description = "Schedule for the alert dispatcher task." diff --git a/examples/simple/snowalert.auto.tfvars.sample b/examples/simple/snowalert.auto.tfvars.sample index c651286..c1c3924 100644 --- a/examples/simple/snowalert.auto.tfvars.sample +++ b/examples/simple/snowalert.auto.tfvars.sample @@ -12,6 +12,7 @@ snowalert_user_name = "APP_SNOWALERT" snowalert_role_name = "APP_SNOWALERT" alerts_merge_schedule = "0 12 * * *" # daily at 12 pm: https://crontab.guru/#0_12_*_*_* +alert_processor_schedule = "0 * * * * " # every hour: https://crontab.guru/#0_*_*_*_* alert_dispatch_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_scheduler_schedule = "1/15 * * * *" # every 15 mins: https://crontab.guru/#1/15_*_*_*_* diff --git a/examples/simple/variables.tf b/examples/simple/variables.tf index 13fe0f2..7f8e5e2 100644 --- a/examples/simple/variables.tf +++ b/examples/simple/variables.tf @@ -162,6 +162,12 @@ variable "alerts_merge_schedule" { default = "0 12 * * *" } +variable "alert_processor_schedule" { + type = string + description = "Schedule for the alert processor task." + default = "0 * * * *" +} + variable "alert_dispatch_schedule" { type = string description = "Schedule for the alert dispatcher task." diff --git a/procedures.tf b/procedures.tf index fad56d9..3dc67b3 100644 --- a/procedures.tf +++ b/procedures.tf @@ -77,7 +77,7 @@ resource "snowflake_procedure" "alert_processor" { arguments { name = "correlation_period_minutes" - type = "NUMBER" + type = "VARCHAR" } return_type = "VARIANT" diff --git a/tasks.tf b/tasks.tf index 49c0396..ce77f96 100644 --- a/tasks.tf +++ b/tasks.tf @@ -32,6 +32,25 @@ resource "snowflake_task" "snowalert_suppression_merge_task" { ] } + +resource "snowflake_task" "alert_processor_task" { + provider = snowflake.security_alerting_role + + warehouse = local.snowalert_warehouse_name + database = local.snowalert_database_name + schema = local.results_schema + name = "ALERT_PROCESSOR" + + schedule = "USING CRON ${var.alert_processor_schedule} UTC" + sql_statement = "CALL ${local.results_schema}.${snowflake_procedure.alert_processor.name}('-60')" + enabled = true + + depends_on = [ + module.snowalert_grants + ] +} + + resource "snowflake_task" "alert_dispatcher_task" { provider = snowflake.security_alerting_role diff --git a/variables.tf b/variables.tf index 92f0128..d9b91cc 100644 --- a/variables.tf +++ b/variables.tf @@ -196,6 +196,12 @@ variable "alerts_merge_schedule" { default = "0 12 * * *" } +variable "alert_processor_schedule" { + type = string + description = "Schedule for the alert processor task." + default = "0 * * * *" +} + variable "alert_dispatch_schedule" { type = string description = "Schedule for the alert dispatcher task." From 9d570f7760ab38475c11811f776db037f66fe0b7 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Tue, 28 Mar 2023 17:14:05 +0530 Subject: [PATCH 13/25] Misc refactor --- procedures.tf | 25 ++++++++++++++++++++++++- procedures_js/alert_processor.js | 27 +++++++++++---------------- tasks.tf | 2 +- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/procedures.tf b/procedures.tf index 3dc67b3..3387ab3 100644 --- a/procedures.tf +++ b/procedures.tf @@ -67,7 +67,30 @@ resource "snowflake_procedure" "alerts_merge" { ] } -resource "snowflake_procedure" "alert_processor" { +resource "snowflake_procedure" "alert_processor_with_default_correlation_period" { + provider = snowflake.security_alerting_role + + database = local.snowalert_database_name + schema = local.results_schema + name = "ALERT_PROCESSOR" + language = "JAVASCRIPT" + + return_type = "VARIANT" + execute_as = "CALLER" + statement = templatefile("${path.module}/procedures_js/alert_processor.js", { + results_alerts_table = join(".", [ + local.snowalert_database_name, + local.results_schema, + local.alerts_table, + ]) + }) + + depends_on = [ + module.snowalert_grants + ] +} + +resource "snowflake_procedure" "alert_processor_with_custom_correlation_period" { provider = snowflake.security_alerting_role database = local.snowalert_database_name diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index f6869f0..044bc96 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -1,6 +1,8 @@ //args var CORRELATION_PERIOD_MINUTES +CORRELATION_PERIOD_MINUTES = CORRELATION_PERIOD_MINUTES || -60 + var alert_correlation_result = [] // library @@ -25,7 +27,7 @@ function exec(sqlText, binds = []) { } GET_CORRELATED_ALERT = ` -SELECT * +SELECT correlation_id FROM ${results_alerts_table} WHERE alert:ACTOR = ? AND (alert:OBJECT::STRING = ? OR alert:ACTION::STRING = ?) @@ -42,7 +44,7 @@ function generate_uuid() { return exec(GENERATE_UUID)[0][['UUID_STRING()']] } -function get_correlation_id(alert) { +function find_related_correlation_id(alert) { if ( 'ACTOR' in alert == false || 'OBJECT' in alert == false || @@ -66,14 +68,9 @@ function get_correlation_id(alert) { action = `'["$${o}"]'` } - match = exec(GET_CORRELATED_ALERT, [actor, object, action, time])[0] + match = exec(GET_CORRELATED_ALERT, [actor, object, action, time]) || {} - correlation_id = - match && 'CORRELATION_ID' in match - ? match['CORRELATION_ID'] - : generate_uuid() - - return correlation_id + return match['CORRELATION_ID'] || generate_uuid() } GET_ALERTS_WITHOUT_CORREALTION_ID = ` @@ -86,16 +83,14 @@ WHERE correlation_id IS NULL UPDATE_ALERT_CORRELATION_ID = ` UPDATE ${results_alerts_table} -SET correlation_id = ? +SET correlation_id = COLLATE(?, UUID_STRING()) WHERE alert:EVENT_TIME > DATEADD(minutes, $${CORRELATION_PERIOD_MINUTES}, ?) AND alert:ALERT_ID = ? ` -UNCORRELATED_ALERTS = exec(GET_ALERTS_WITHOUT_CORREALTION_ID) - -for (const x of UNCORRELATED_ALERTS) { - alert_body = x['ALERT'] - correlation_id = get_correlation_id(alert_body) +for (const row of exec(GET_ALERTS_WITHOUT_CORREALTION_ID)) { + alert_body = row['ALERT'] + correlation_id = find_related_correlation_id(alert_body) event_time = String(alert_body['EVENT_TIME']) alert_id = alert_body['ALERT_ID'] @@ -104,4 +99,4 @@ for (const x of UNCORRELATED_ALERTS) { ) } -return { alert_correlation_result: alert_correlation_result } +return { alert_correlation_result } diff --git a/tasks.tf b/tasks.tf index ce77f96..608cccf 100644 --- a/tasks.tf +++ b/tasks.tf @@ -42,7 +42,7 @@ resource "snowflake_task" "alert_processor_task" { name = "ALERT_PROCESSOR" schedule = "USING CRON ${var.alert_processor_schedule} UTC" - sql_statement = "CALL ${local.results_schema}.${snowflake_procedure.alert_processor.name}('-60')" + sql_statement = "CALL ${local.results_schema}.${snowflake_procedure.alert_processor_with_default_correlation_period.name}()" enabled = true depends_on = [ From ce43a2e1b5d31df42df1e2d17be76e80a99c5d18 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Tue, 28 Mar 2023 17:16:51 +0530 Subject: [PATCH 14/25] Update alert processor default schedule --- examples/complete/snowalert.auto.tfvars.sample | 2 +- examples/complete/variables.tf | 2 +- examples/simple/snowalert.auto.tfvars.sample | 2 +- examples/simple/variables.tf | 2 +- variables.tf | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/complete/snowalert.auto.tfvars.sample b/examples/complete/snowalert.auto.tfvars.sample index ae5e9c3..5542078 100644 --- a/examples/complete/snowalert.auto.tfvars.sample +++ b/examples/complete/snowalert.auto.tfvars.sample @@ -10,7 +10,7 @@ snowalert_warehouse_name = "SNOWALERT_WAREHOUSE" snowalert_database_name = "SNOWALERT" alerts_merge_schedule = "0 12 * * *" # daily at 12 pm: https://crontab.guru/#0_12_*_*_* -alert_processor_schedule = "0 * * * * " # every hour: https://crontab.guru/#0_*_*_*_* +alert_processor_schedule = "* * * * * " # every hour: https://crontab.guru/#*_*_*_*_* alert_dispatch_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_scheduler_schedule = "1/15 * * * *" # every 15 mins: https://crontab.guru/#1/15_*_*_*_* diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index 38e7d48..71a07cf 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -165,7 +165,7 @@ variable "alerts_merge_schedule" { variable "alert_processor_schedule" { type = string description = "Schedule for the alert processor task." - default = "0 * * * *" + default = "* * * * *" } variable "alert_dispatch_schedule" { diff --git a/examples/simple/snowalert.auto.tfvars.sample b/examples/simple/snowalert.auto.tfvars.sample index c1c3924..505ac3b 100644 --- a/examples/simple/snowalert.auto.tfvars.sample +++ b/examples/simple/snowalert.auto.tfvars.sample @@ -12,7 +12,7 @@ snowalert_user_name = "APP_SNOWALERT" snowalert_role_name = "APP_SNOWALERT" alerts_merge_schedule = "0 12 * * *" # daily at 12 pm: https://crontab.guru/#0_12_*_*_* -alert_processor_schedule = "0 * * * * " # every hour: https://crontab.guru/#0_*_*_*_* +alert_processor_schedule = "* * * * * " # every hour: https://crontab.guru/#0_*_*_*_* alert_dispatch_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_scheduler_schedule = "1/15 * * * *" # every 15 mins: https://crontab.guru/#1/15_*_*_*_* diff --git a/examples/simple/variables.tf b/examples/simple/variables.tf index 7f8e5e2..72b15d4 100644 --- a/examples/simple/variables.tf +++ b/examples/simple/variables.tf @@ -165,7 +165,7 @@ variable "alerts_merge_schedule" { variable "alert_processor_schedule" { type = string description = "Schedule for the alert processor task." - default = "0 * * * *" + default = "* * * * *" } variable "alert_dispatch_schedule" { diff --git a/variables.tf b/variables.tf index d9b91cc..ab18d79 100644 --- a/variables.tf +++ b/variables.tf @@ -199,7 +199,7 @@ variable "alerts_merge_schedule" { variable "alert_processor_schedule" { type = string description = "Schedule for the alert processor task." - default = "0 * * * *" + default = "* * * * *" } variable "alert_dispatch_schedule" { From 29e1f9de3805573710ff35730b610464be07704d Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Tue, 28 Mar 2023 17:53:08 +0530 Subject: [PATCH 15/25] Update alert_processor.js --- procedures_js/alert_processor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index 044bc96..fd58ac2 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -83,7 +83,7 @@ WHERE correlation_id IS NULL UPDATE_ALERT_CORRELATION_ID = ` UPDATE ${results_alerts_table} -SET correlation_id = COLLATE(?, UUID_STRING()) +SET correlation_id = COALESCE(?, UUID_STRING()) WHERE alert:EVENT_TIME > DATEADD(minutes, $${CORRELATION_PERIOD_MINUTES}, ?) AND alert:ALERT_ID = ? ` From 0c84ad5c511826f4e129027e427b55f7f402691b Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Wed, 29 Mar 2023 16:51:53 +0530 Subject: [PATCH 16/25] Update alert_processor.js --- procedures_js/alert_processor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index fd58ac2..b861fa6 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -41,7 +41,7 @@ LIMIT 1 function generate_uuid() { GENERATE_UUID = `SELECT UUID_STRING()` - return exec(GENERATE_UUID)[0][['UUID_STRING()']] + return exec(GENERATE_UUID)[0]['UUID_STRING()'] } function find_related_correlation_id(alert) { From 180d12888227a97af5ae1da80794a5fcb2b8f04b Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Wed, 29 Mar 2023 17:01:07 +0530 Subject: [PATCH 17/25] Update alert_processor.js --- procedures_js/alert_processor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index b861fa6..c6e23fb 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -68,7 +68,7 @@ function find_related_correlation_id(alert) { action = `'["$${o}"]'` } - match = exec(GET_CORRELATED_ALERT, [actor, object, action, time]) || {} + match = exec(GET_CORRELATED_ALERT, [actor, object, action, time])[0] || {} return match['CORRELATION_ID'] || generate_uuid() } From 1134e4c22b386d59b7f0b0cccddb6374e75b5a47 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Wed, 29 Mar 2023 17:25:30 +0530 Subject: [PATCH 18/25] Update alert_processor.js --- procedures_js/alert_processor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index c6e23fb..ab8b737 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -61,11 +61,11 @@ function find_related_correlation_id(alert) { if (object instanceof Array) { o = object.join('","') - object = `'["$${o}"]'` + object = `["$${o}"]` } if (action instanceof Array) { o = action.join('","') - action = `'["$${o}"]'` + action = `["$${o}"]` } match = exec(GET_CORRELATED_ALERT, [actor, object, action, time])[0] || {} From 2970cbf381000d5026faedae9cc13bf4b5c4bb04 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Wed, 29 Mar 2023 17:42:51 +0530 Subject: [PATCH 19/25] Update tfvars sample --- examples/complete/snowalert.auto.tfvars.sample | 2 +- examples/simple/snowalert.auto.tfvars.sample | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/complete/snowalert.auto.tfvars.sample b/examples/complete/snowalert.auto.tfvars.sample index 5542078..929e515 100644 --- a/examples/complete/snowalert.auto.tfvars.sample +++ b/examples/complete/snowalert.auto.tfvars.sample @@ -10,7 +10,7 @@ snowalert_warehouse_name = "SNOWALERT_WAREHOUSE" snowalert_database_name = "SNOWALERT" alerts_merge_schedule = "0 12 * * *" # daily at 12 pm: https://crontab.guru/#0_12_*_*_* -alert_processor_schedule = "* * * * * " # every hour: https://crontab.guru/#*_*_*_*_* +alert_processor_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_dispatch_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_scheduler_schedule = "1/15 * * * *" # every 15 mins: https://crontab.guru/#1/15_*_*_*_* diff --git a/examples/simple/snowalert.auto.tfvars.sample b/examples/simple/snowalert.auto.tfvars.sample index 505ac3b..2fb366c 100644 --- a/examples/simple/snowalert.auto.tfvars.sample +++ b/examples/simple/snowalert.auto.tfvars.sample @@ -12,7 +12,7 @@ snowalert_user_name = "APP_SNOWALERT" snowalert_role_name = "APP_SNOWALERT" alerts_merge_schedule = "0 12 * * *" # daily at 12 pm: https://crontab.guru/#0_12_*_*_* -alert_processor_schedule = "* * * * * " # every hour: https://crontab.guru/#0_*_*_*_* +alert_processor_schedule = "* * * * * " # every min: https://crontab.guru/#0_*_*_*_* alert_dispatch_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_scheduler_schedule = "1/15 * * * *" # every 15 mins: https://crontab.guru/#1/15_*_*_*_* From 9f238132e924a5d2119a3effdcd2170b819c5f5b Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Wed, 29 Mar 2023 17:57:48 +0530 Subject: [PATCH 20/25] Update alert_processor.js --- procedures_js/alert_processor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index ab8b737..28733de 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -73,7 +73,7 @@ function find_related_correlation_id(alert) { return match['CORRELATION_ID'] || generate_uuid() } -GET_ALERTS_WITHOUT_CORREALTION_ID = ` +GET_ALERTS_WITHOUT_CORRELATION_ID = ` SELECT * FROM ${results_alerts_table} WHERE correlation_id IS NULL @@ -88,7 +88,7 @@ WHERE alert:EVENT_TIME > DATEADD(minutes, $${CORRELATION_PERIOD_MINUTES}, ?) AND alert:ALERT_ID = ? ` -for (const row of exec(GET_ALERTS_WITHOUT_CORREALTION_ID)) { +for (const row of exec(GET_ALERTS_WITHOUT_CORRELATION_ID)) { alert_body = row['ALERT'] correlation_id = find_related_correlation_id(alert_body) event_time = String(alert_body['EVENT_TIME']) From 19e8abef84ef9e2ee24ddc550a6fbcebee6f70d8 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Mon, 3 Apr 2023 18:44:34 +0530 Subject: [PATCH 21/25] Update alert_processor.js --- procedures_js/alert_processor.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index 28733de..af7d543 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -94,9 +94,14 @@ for (const row of exec(GET_ALERTS_WITHOUT_CORRELATION_ID)) { event_time = String(alert_body['EVENT_TIME']) alert_id = alert_body['ALERT_ID'] - alert_correlation_result.push( - exec(UPDATE_ALERT_CORRELATION_ID, [correlation_id, event_time, alert_id]) - ) + alert_correlation_result.push({ + alert_id: alert_id, + alert_correlation_result: exec(UPDATE_ALERT_CORRELATION_ID, [ + correlation_id, + event_time, + alert_id, + ]), + }) } -return { alert_correlation_result } +return alert_correlation_result From 2b19dd092f29903a38d358b33638a4035aec5308 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Mon, 3 Apr 2023 18:53:52 +0530 Subject: [PATCH 22/25] Rename variable --- procedures_js/alert_processor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index af7d543..dca4ed6 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -3,7 +3,7 @@ var CORRELATION_PERIOD_MINUTES CORRELATION_PERIOD_MINUTES = CORRELATION_PERIOD_MINUTES || -60 -var alert_correlation_result = [] +var alert_correlation_result_array = [] // library function exec(sqlText, binds = []) { @@ -94,7 +94,7 @@ for (const row of exec(GET_ALERTS_WITHOUT_CORRELATION_ID)) { event_time = String(alert_body['EVENT_TIME']) alert_id = alert_body['ALERT_ID'] - alert_correlation_result.push({ + alert_correlation_result_array.push({ alert_id: alert_id, alert_correlation_result: exec(UPDATE_ALERT_CORRELATION_ID, [ correlation_id, @@ -104,4 +104,4 @@ for (const row of exec(GET_ALERTS_WITHOUT_CORRELATION_ID)) { }) } -return alert_correlation_result +return alert_correlation_result_array From 209cd20e088a5eb0c1f451694f69df96e811e4d6 Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Mon, 3 Apr 2023 21:54:58 +0530 Subject: [PATCH 23/25] TF changes to run the correlation task after the suppressor --- examples/complete/main.tf | 1 - examples/complete/snowalert.auto.tfvars.sample | 1 - examples/complete/variables.tf | 6 ------ examples/simple/snowalert.auto.tfvars.sample | 1 - examples/simple/variables.tf | 6 ------ tasks.tf | 2 +- variables.tf | 6 ------ 7 files changed, 1 insertion(+), 22 deletions(-) diff --git a/examples/complete/main.tf b/examples/complete/main.tf index afc5738..6940ee5 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -21,7 +21,6 @@ module "snowalert" { snowalert_database_name = var.snowalert_database_name alerts_merge_schedule = var.alerts_merge_schedule - alert_processor_schedule = var.alert_processor_schedule alert_dispatch_schedule = var.alert_dispatch_schedule alert_scheduler_schedule = var.alert_scheduler_schedule diff --git a/examples/complete/snowalert.auto.tfvars.sample b/examples/complete/snowalert.auto.tfvars.sample index 929e515..20d7d33 100644 --- a/examples/complete/snowalert.auto.tfvars.sample +++ b/examples/complete/snowalert.auto.tfvars.sample @@ -10,7 +10,6 @@ snowalert_warehouse_name = "SNOWALERT_WAREHOUSE" snowalert_database_name = "SNOWALERT" alerts_merge_schedule = "0 12 * * *" # daily at 12 pm: https://crontab.guru/#0_12_*_*_* -alert_processor_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_dispatch_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_scheduler_schedule = "1/15 * * * *" # every 15 mins: https://crontab.guru/#1/15_*_*_*_* diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index 71a07cf..bdc8bbc 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -162,12 +162,6 @@ variable "alerts_merge_schedule" { default = "0 12 * * *" } -variable "alert_processor_schedule" { - type = string - description = "Schedule for the alert processor task." - default = "* * * * *" -} - variable "alert_dispatch_schedule" { type = string description = "Schedule for the alert dispatcher task." diff --git a/examples/simple/snowalert.auto.tfvars.sample b/examples/simple/snowalert.auto.tfvars.sample index 2fb366c..c651286 100644 --- a/examples/simple/snowalert.auto.tfvars.sample +++ b/examples/simple/snowalert.auto.tfvars.sample @@ -12,7 +12,6 @@ snowalert_user_name = "APP_SNOWALERT" snowalert_role_name = "APP_SNOWALERT" alerts_merge_schedule = "0 12 * * *" # daily at 12 pm: https://crontab.guru/#0_12_*_*_* -alert_processor_schedule = "* * * * * " # every min: https://crontab.guru/#0_*_*_*_* alert_dispatch_schedule = "* * * * * " # every min: https://crontab.guru/#*_*_*_*_* alert_scheduler_schedule = "1/15 * * * *" # every 15 mins: https://crontab.guru/#1/15_*_*_*_* diff --git a/examples/simple/variables.tf b/examples/simple/variables.tf index 72b15d4..13fe0f2 100644 --- a/examples/simple/variables.tf +++ b/examples/simple/variables.tf @@ -162,12 +162,6 @@ variable "alerts_merge_schedule" { default = "0 12 * * *" } -variable "alert_processor_schedule" { - type = string - description = "Schedule for the alert processor task." - default = "* * * * *" -} - variable "alert_dispatch_schedule" { type = string description = "Schedule for the alert dispatcher task." diff --git a/tasks.tf b/tasks.tf index 608cccf..6cdd57a 100644 --- a/tasks.tf +++ b/tasks.tf @@ -41,7 +41,7 @@ resource "snowflake_task" "alert_processor_task" { schema = local.results_schema name = "ALERT_PROCESSOR" - schedule = "USING CRON ${var.alert_processor_schedule} UTC" + after = [snowflake_task.snowalert_suppression_merge_task.name] sql_statement = "CALL ${local.results_schema}.${snowflake_procedure.alert_processor_with_default_correlation_period.name}()" enabled = true diff --git a/variables.tf b/variables.tf index ab18d79..92f0128 100644 --- a/variables.tf +++ b/variables.tf @@ -196,12 +196,6 @@ variable "alerts_merge_schedule" { default = "0 12 * * *" } -variable "alert_processor_schedule" { - type = string - description = "Schedule for the alert processor task." - default = "* * * * *" -} - variable "alert_dispatch_schedule" { type = string description = "Schedule for the alert dispatcher task." From 3c742dce93435e17fd55c5d53a177ecefb6adf0b Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Tue, 11 Apr 2023 17:37:34 +0530 Subject: [PATCH 24/25] Return null instead of generate_uuid when finding correlation id --- procedures_js/alert_processor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index dca4ed6..75c9e81 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -51,7 +51,7 @@ function find_related_correlation_id(alert) { 'ACTION' in alert == false || 'EVENT_TIME' in alert == false ) { - return generate_uuid() + return null } actor = alert['ACTOR'] @@ -70,7 +70,7 @@ function find_related_correlation_id(alert) { match = exec(GET_CORRELATED_ALERT, [actor, object, action, time])[0] || {} - return match['CORRELATION_ID'] || generate_uuid() + return match['CORRELATION_ID'] || null } GET_ALERTS_WITHOUT_CORRELATION_ID = ` From c1d02b22f0ec3dd1e42afc24a274e4ca65b43c2e Mon Sep 17 00:00:00 2001 From: sfc-gh-nlele Date: Tue, 11 Apr 2023 17:47:26 +0530 Subject: [PATCH 25/25] Remove unnneded generate_uuid function --- procedures_js/alert_processor.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/procedures_js/alert_processor.js b/procedures_js/alert_processor.js index 75c9e81..2466ea4 100644 --- a/procedures_js/alert_processor.js +++ b/procedures_js/alert_processor.js @@ -39,11 +39,6 @@ ORDER BY event_time DESC LIMIT 1 ` -function generate_uuid() { - GENERATE_UUID = `SELECT UUID_STRING()` - return exec(GENERATE_UUID)[0]['UUID_STRING()'] -} - function find_related_correlation_id(alert) { if ( 'ACTOR' in alert == false ||