diff --git a/google_secops_parser/README.md b/google_secops_parser/README.md index afe3a61..1134551 100644 --- a/google_secops_parser/README.md +++ b/google_secops_parser/README.md @@ -48,17 +48,23 @@ DMARC types. ## Caveats 1. **Unvalidated** — see [Status](#status). -2. **JSON type handling** — parsedmarc emits `dmarc_aligned` / `spf_aligned` / - `dkim_aligned` / `testing` / `normalized_timespan` as JSON booleans and - `count` / `*_session_count` / `source_asn` as numbers. Chronicle's `json{}` - filter **preserves the original JSON type**, so the parser explicitly - converts these to strings (`mutate { convert => { … => "string" } }`) before - any comparison — otherwise `[dmarc_aligned] == "false"` would never match. - Relatedly, every field tested in an `if` is initialized to `""` *before* the - `json` filter, because CBN raises `_failed_parsing_` on a conditional that - references a field absent from the log. A DMARC-fail record - (`dmarc_aligned=false`) should yield `security_result.category = - AUTH_VIOLATION` — still worth confirming in the validation tool. +2. **JSON types** — Chronicle's `json{}` filter **preserves the original JSON + type**, so parsedmarc's booleans and numbers are handled differently: + - **Booleans** (`dmarc_aligned` / `spf_aligned` / `dkim_aligned` / `testing` + / `normalized_timespan`) are converted to strings so `[dmarc_aligned] == + "false"` works, and stored as `string_value` (Google's content-hub parsers + never use `bool_value`). + - **Numbers** (`count` / `*_session_count` / `source_asn`) are stored as + `number_value` — built as a string, `convert`-ed to `uinteger`, then + renamed — so SecOps can range-query and sort them (parsedmarc's "store + numbers as numbers" rule). + + Every `if`-tested field is initialized to `""` *before* `json` and guarded + with `!= ""`: CBN raises `_failed_parsing_` on a conditional referencing an + absent field, and treats an initialized-but-empty field as present. A + DMARC-fail record (`dmarc_aligned=false`) should yield + `security_result.category = AUTH_VIOLATION` — worth confirming in the + validation tool. 3. **Aggregate count** — a DMARC aggregate record summarizes `count` messages from one source IP, not a single message. Each record becomes one `EMAIL_TRANSACTION` with `count` carried in `additional.fields`. There is no diff --git a/google_secops_parser/parsedmarc.conf b/google_secops_parser/parsedmarc.conf index 49391ae..ca5c13b 100644 --- a/google_secops_parser/parsedmarc.conf +++ b/google_secops_parser/parsedmarc.conf @@ -50,11 +50,16 @@ filter { # run through the SecOps parser-validation tool against a live tenant. # Validate with the sample events in README.md before production use. # 2. JSON TYPES ARE PRESERVED. The CBN json{} filter keeps the original JSON - # type (a JSON boolean stays a boolean, a number stays a number), so - # parsedmarc's boolean *_aligned / testing / normalized_timespan and - # numeric count / *_count would NOT match string comparisons. This parser - # converts them to strings (step 1b) before any `== "true"` / `== "false"` - # test or %{...} use -- as Google's official content-hub parsers do. + # type (a JSON boolean stays a boolean, a number stays a number). + # Booleans (*_aligned / testing / normalized_timespan) are converted to + # strings (step 1b) so `== "true"` / `== "false"` tests work, and stored + # as string_value (content-hub never uses bool_value). Numbers (count / + # *_session_count / source_asn) are stored as number_value -- built as a + # string, convert-ed to uinteger, then renamed to number_value -- so + # SecOps can range-query and sort them. Matches the content-hub parsers. + # 2b. CONDITIONALS. Every field tested in an `if` is initialized to "" in + # step 1a, and guards use `if [field] != ""` (not bare `if [field]`, + # which is true for an initialized-but-empty field). Matches content-hub. # 3. AGGREGATE COUNT. A DMARC aggregate record summarizes "count" messages # from one source IP, not a single message. Each becomes one # EMAIL_TRANSACTION with "count" carried in additional.fields; there is no @@ -184,11 +189,14 @@ filter { } # --------------------------------------------------------------------------- - # 1b. Convert parsedmarc's JSON booleans/numbers to strings. The json{} filter - # PRESERVES the original JSON type, so without this, [dmarc_aligned] is a - # boolean and `== "false"` never matches (and %{count} on an int is - # unreliable). Fields left as "" by step 1a convert as a harmless no-op. - # Google's official content-hub parsers use mutate{convert} the same way. + # 1b. Convert parsedmarc's JSON booleans to strings so they can be compared. + # The json{} filter PRESERVES the original JSON type, so without this + # [dmarc_aligned] is a boolean and `== "false"` never matches. Booleans are + # stored as string_value (matching Google's content-hub parsers, which + # never use bool_value). Numeric fields are deliberately NOT converted + # here -- they are stored as number_value (not string) in additional.fields + # so SecOps can range-query and sort them; see the count / *_session_count + # / source_asn blocks below. # --------------------------------------------------------------------------- mutate { convert => { @@ -197,10 +205,6 @@ filter { "dkim_aligned" => "string" "testing" => "string" "normalized_timespan" => "string" - "count" => "string" - "source_asn" => "string" - "successful_session_count" => "string" - "failed_session_count" => "string" } on_error => "convert_error" } @@ -218,11 +222,11 @@ filter { # (a generic name), and dmarc_aligned (a boolean that only becomes testable # after the convert in step 1b -- detection should not depend on that). # --------------------------------------------------------------------------- - if [feedback_type] { + if [feedback_type] != "" { mutate { replace => { "report_type" => "failure" } } - } else if [policy_type] { + } else if [policy_type] != "" { mutate { replace => { "report_type" => "smtp_tls" } } - } else if [xml_schema] { + } else if [xml_schema] != "" { mutate { replace => { "report_type" => "aggregate" } } } @@ -238,7 +242,7 @@ filter { mutate { replace => { "event_type" => "EMAIL_TRANSACTION" } } - if [report_id] { + if [report_id] != "" { mutate { replace => { "event.idm.read_only_udm.metadata.product_log_id" => "%{report_id}" @@ -247,7 +251,7 @@ filter { } # -- event timestamp: start of the record's reporting interval (UTC). -- - if [begin_date] { + if [begin_date] != "" { date { match => ["begin_date", "yyyy-MM-dd HH:mm:ss"] timezone => "UTC" @@ -256,20 +260,20 @@ filter { } # -- principal: the sending source (machine details only). -- - if [source_ip_address] { + if [source_ip_address] != "" { mutate { merge => { "event.idm.read_only_udm.principal.ip" => "source_ip_address" } on_error => "agg_src_ip_error" } } - if [source_reverse_dns] { + if [source_reverse_dns] != "" { mutate { replace => { "event.idm.read_only_udm.principal.hostname" => "%{source_reverse_dns}" } } } - if [source_country] { + if [source_country] != "" { mutate { replace => { "event.idm.read_only_udm.principal.location.country_or_region" => "%{source_country}" @@ -278,7 +282,7 @@ filter { } # -- target: the domain the report is about. -- - if [domain] { + if [domain] != "" { mutate { replace => { "event.idm.read_only_udm.target.hostname" => "%{domain}" @@ -287,7 +291,7 @@ filter { } # -- email: aggregate only carries the From domain (see caveat 4). -- - if [header_from] { + if [header_from] != "" { mutate { replace => { "event.idm.read_only_udm.network.email.from" => "%{header_from}" @@ -325,73 +329,78 @@ filter { } # -- additional.fields: DMARC dimensions a dashboard would filter on. -- - if [org_name] { + if [org_name] != "" { mutate { replace => { "f_org_name.key" => "org_name" "f_org_name.value.string_value" => "%{org_name}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_org_name" } } } - if [org_email] { + if [org_email] != "" { mutate { replace => { "f_org_email.key" => "org_email" "f_org_email.value.string_value" => "%{org_email}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_org_email" } } } - if [begin_date] { + if [begin_date] != "" { mutate { replace => { "f_begin.key" => "begin_date" "f_begin.value.string_value" => "%{begin_date}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_begin" } } } - if [end_date] { + if [end_date] != "" { mutate { replace => { "f_end.key" => "end_date" "f_end.value.string_value" => "%{end_date}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_end" } } } - if [count] { - mutate { - replace => { "f_count.key" => "count" "f_count.value.string_value" => "%{count}" } - merge => { "event.idm.read_only_udm.additional.fields" => "f_count" } - } + mutate { + replace => { "f_count.key" => "count" "f_count.value.string_value" => "%{count}" } } - if [p] { + mutate { + convert => { "f_count.value.string_value" => "uinteger" } + on_error => "count_nan" + } + if ![count_nan] { + mutate { rename => { "f_count.value.string_value" => "f_count.value.number_value" } } + mutate { merge => { "event.idm.read_only_udm.additional.fields" => "f_count" } } + } + if [p] != "" { mutate { replace => { "f_p.key" => "dmarc_policy" "f_p.value.string_value" => "%{p}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_p" } } } - if [sp] { + if [sp] != "" { mutate { replace => { "f_sp.key" => "dmarc_subdomain_policy" "f_sp.value.string_value" => "%{sp}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_sp" } } } - if [np] { + if [np] != "" { mutate { replace => { "f_np.key" => "dmarc_np_policy" "f_np.value.string_value" => "%{np}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_np" } } } - if [pct] { + if [pct] != "" { mutate { replace => { "f_pct.key" => "dmarc_pct" "f_pct.value.string_value" => "%{pct}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_pct" } } } - if [fo] { + if [fo] != "" { mutate { replace => { "f_fo.key" => "dmarc_fo" "f_fo.value.string_value" => "%{fo}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_fo" } } } - if [adkim] { + if [adkim] != "" { mutate { replace => { "f_adkim.key" => "dkim_alignment_mode" "f_adkim.value.string_value" => "%{adkim}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_adkim" } } } - if [aspf] { + if [aspf] != "" { mutate { replace => { "f_aspf.key" => "spf_alignment_mode" "f_aspf.value.string_value" => "%{aspf}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_aspf" } @@ -403,7 +412,7 @@ filter { merge => { "event.idm.read_only_udm.additional.fields" => "f_testing" } } } - if [discovery_method] { + if [discovery_method] != "" { mutate { replace => { "f_disc.key" => "discovery_method" "f_disc.value.string_value" => "%{discovery_method}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_disc" } @@ -435,7 +444,7 @@ filter { merge => { "event.idm.read_only_udm.additional.fields" => "f_dkima" } } } - if [disposition] { + if [disposition] != "" { mutate { replace => { "f_disp.key" => "disposition" "f_disp.value.string_value" => "%{disposition}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_disp" } @@ -443,37 +452,37 @@ filter { } # DKIM / SPF auth detail (comma-joined by parsedmarc). - if [dkim_domains] { + if [dkim_domains] != "" { mutate { replace => { "f_dkd.key" => "dkim_domains" "f_dkd.value.string_value" => "%{dkim_domains}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_dkd" } } } - if [dkim_selectors] { + if [dkim_selectors] != "" { mutate { replace => { "f_dks.key" => "dkim_selectors" "f_dks.value.string_value" => "%{dkim_selectors}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_dks" } } } - if [dkim_results] { + if [dkim_results] != "" { mutate { replace => { "f_dkr.key" => "dkim_results" "f_dkr.value.string_value" => "%{dkim_results}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_dkr" } } } - if [spf_domains] { + if [spf_domains] != "" { mutate { replace => { "f_spd.key" => "spf_domains" "f_spd.value.string_value" => "%{spf_domains}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_spd" } } } - if [spf_scopes] { + if [spf_scopes] != "" { mutate { replace => { "f_sps.key" => "spf_scopes" "f_sps.value.string_value" => "%{spf_scopes}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_sps" } } } - if [spf_results] { + if [spf_results] != "" { mutate { replace => { "f_spr.key" => "spf_results" "f_spr.value.string_value" => "%{spf_results}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_spr" } @@ -481,13 +490,13 @@ filter { } # Policy overrides (parsedmarc writes the literal "none" when there are none). - if [policy_override_reasons] and [policy_override_reasons] != "none" { + if [policy_override_reasons] != "" and [policy_override_reasons] != "none" { mutate { replace => { "f_por.key" => "policy_override_reasons" "f_por.value.string_value" => "%{policy_override_reasons}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_por" } } } - if [policy_override_comments] and [policy_override_comments] != "none" { + if [policy_override_comments] != "" and [policy_override_comments] != "none" { mutate { replace => { "f_poc.key" => "policy_override_comments" "f_poc.value.string_value" => "%{policy_override_comments}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_poc" } @@ -495,37 +504,42 @@ filter { } # Source enrichment from parsedmarc's reverse-DNS / MMDB maps. - if [source_base_domain] { + if [source_base_domain] != "" { mutate { replace => { "f_sbd.key" => "source_base_domain" "f_sbd.value.string_value" => "%{source_base_domain}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_sbd" } } } - if [source_name] { + if [source_name] != "" { mutate { replace => { "f_sn.key" => "source_name" "f_sn.value.string_value" => "%{source_name}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_sn" } } } - if [source_type] { + if [source_type] != "" { mutate { replace => { "f_st.key" => "source_type" "f_st.value.string_value" => "%{source_type}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_st" } } } - if [source_asn] { - mutate { - replace => { "f_asn.key" => "source_asn" "f_asn.value.string_value" => "%{source_asn}" } - merge => { "event.idm.read_only_udm.additional.fields" => "f_asn" } - } + mutate { + replace => { "f_asn.key" => "source_asn" "f_asn.value.string_value" => "%{source_asn}" } } - if [source_as_name] { + mutate { + convert => { "f_asn.value.string_value" => "uinteger" } + on_error => "asn_nan" + } + if ![asn_nan] { + mutate { rename => { "f_asn.value.string_value" => "f_asn.value.number_value" } } + mutate { merge => { "event.idm.read_only_udm.additional.fields" => "f_asn" } } + } + if [source_as_name] != "" { mutate { replace => { "f_asnm.key" => "source_as_name" "f_asnm.value.string_value" => "%{source_as_name}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_asnm" } } } - if [source_as_domain] { + if [source_as_domain] != "" { mutate { replace => { "f_asd.key" => "source_as_domain" "f_asd.value.string_value" => "%{source_as_domain}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_asd" } @@ -533,13 +547,13 @@ filter { } # Envelope identifiers (usually empty in aggregate reports). - if [envelope_from] { + if [envelope_from] != "" { mutate { replace => { "f_ef.key" => "envelope_from" "f_ef.value.string_value" => "%{envelope_from}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_ef" } } } - if [envelope_to] { + if [envelope_to] != "" { mutate { replace => { "f_et.key" => "envelope_to" "f_et.value.string_value" => "%{envelope_to}" } merge => { "event.idm.read_only_udm.additional.fields" => "f_et" } @@ -554,7 +568,7 @@ filter { mutate { replace => { "event_type" => "EMAIL_TRANSACTION" } } - if [message_id] { + if [message_id] != "" { mutate { replace => { "event.idm.read_only_udm.metadata.product_log_id" => "%{message_id}" @@ -563,7 +577,7 @@ filter { } # -- event timestamp: message arrival time (UTC). -- - if [arrival_date_utc] { + if [arrival_date_utc] != "" { date { match => ["arrival_date_utc", "yyyy-MM-dd HH:mm:ss"] timezone => "UTC" @@ -572,20 +586,20 @@ filter { } # -- principal: the sending source. -- - if [source_ip_address] { + if [source_ip_address] != "" { mutate { merge => { "event.idm.read_only_udm.principal.ip" => "source_ip_address" } on_error => "fail_src_ip_error" } } - if [source_reverse_dns] { + if [source_reverse_dns] != "" { mutate { replace => { "event.idm.read_only_udm.principal.hostname" => "%{source_reverse_dns}" } } } - if [source_country] { + if [source_country] != "" { mutate { replace => { "event.idm.read_only_udm.principal.location.country_or_region" => "%{source_country}" @@ -594,7 +608,7 @@ filter { } # -- target: the reported (claimed) domain. -- - if [reported_domain] { + if [reported_domain] != "" { mutate { replace => { "event.idm.read_only_udm.target.hostname" => "%{reported_domain}" @@ -603,14 +617,14 @@ filter { } # -- email: failure reports carry full addresses + subject. -- - if [original_mail_from] { + if [original_mail_from] != "" { mutate { replace => { "event.idm.read_only_udm.network.email.from" => "%{original_mail_from}" } } } - if [original_rcpt_to] { + if [original_rcpt_to] != "" { mutate { replace => { "event.idm.read_only_udm.network.email.to" => "%{original_rcpt_to}" @@ -618,14 +632,14 @@ filter { on_error => "fail_rcpt_error" } } - if [subject] { + if [subject] != "" { mutate { replace => { "event.idm.read_only_udm.network.email.subject" => "%{subject}" } } } - if [message_id] { + if [message_id] != "" { mutate { replace => { "event.idm.read_only_udm.network.email.mail_id" => "%{message_id}" @@ -649,7 +663,7 @@ filter { } else { mutate { replace => { "srf.action" => "UNKNOWN_ACTION" } } } - if [auth_failure] { + if [auth_failure] != "" { mutate { replace => { "srf.description" => "auth_failure=%{auth_failure} delivery_result=%{delivery_result}" @@ -662,49 +676,49 @@ filter { } # -- additional.fields. -- - if [feedback_type] { + if [feedback_type] != "" { mutate { replace => { "g_fb.key" => "feedback_type" "g_fb.value.string_value" => "%{feedback_type}" } merge => { "event.idm.read_only_udm.additional.fields" => "g_fb" } } } - if [auth_failure] { + if [auth_failure] != "" { mutate { replace => { "g_af.key" => "auth_failure" "g_af.value.string_value" => "%{auth_failure}" } merge => { "event.idm.read_only_udm.additional.fields" => "g_af" } } } - if [delivery_result] { + if [delivery_result] != "" { mutate { replace => { "g_dr.key" => "delivery_result" "g_dr.value.string_value" => "%{delivery_result}" } merge => { "event.idm.read_only_udm.additional.fields" => "g_dr" } } } - if [authentication_results] { + if [authentication_results] != "" { mutate { replace => { "g_ar.key" => "authentication_results" "g_ar.value.string_value" => "%{authentication_results}" } merge => { "event.idm.read_only_udm.additional.fields" => "g_ar" } } } - if [authentication_mechanisms] { + if [authentication_mechanisms] != "" { mutate { replace => { "g_am.key" => "authentication_mechanisms" "g_am.value.string_value" => "%{authentication_mechanisms}" } merge => { "event.idm.read_only_udm.additional.fields" => "g_am" } } } - if [user_agent] { + if [user_agent] != "" { mutate { replace => { "g_ua.key" => "user_agent" "g_ua.value.string_value" => "%{user_agent}" } merge => { "event.idm.read_only_udm.additional.fields" => "g_ua" } } } - if [dkim_domain] { + if [dkim_domain] != "" { mutate { replace => { "g_dd.key" => "dkim_domain" "g_dd.value.string_value" => "%{dkim_domain}" } merge => { "event.idm.read_only_udm.additional.fields" => "g_dd" } } } - if [arrival_date] { + if [arrival_date] != "" { mutate { replace => { "g_ad.key" => "arrival_date" "g_ad.value.string_value" => "%{arrival_date}" } merge => { "event.idm.read_only_udm.additional.fields" => "g_ad" } @@ -722,7 +736,7 @@ filter { mutate { replace => { "event_type" => "GENERIC_EVENT" } } - if [report_id] { + if [report_id] != "" { mutate { replace => { "event.idm.read_only_udm.metadata.product_log_id" => "%{report_id}" @@ -732,7 +746,7 @@ filter { # -- event timestamp: start of the report window. SMTP TLS uses ISO 8601 # with a trailing Z (e.g. 2025-12-07T19:00:00Z), unlike the other types. -- - if [begin_date] { + if [begin_date] != "" { date { match => ["begin_date", "yyyy-MM-dd'T'HH:mm:ss'Z'"] timezone => "UTC" @@ -741,7 +755,7 @@ filter { } # -- target: the reported policy domain (always present -> the noun). -- - if [policy_domain] { + if [policy_domain] != "" { mutate { replace => { "event.idm.read_only_udm.target.hostname" => "%{policy_domain}" @@ -749,14 +763,14 @@ filter { } } # receiving MTA IP, when a per-session failure row carries it. - if [receiving_ip] { + if [receiving_ip] != "" { mutate { merge => { "event.idm.read_only_udm.target.ip" => "receiving_ip" } on_error => "tls_rcv_ip_error" } } # sending MTA IP, when present, is the principal. - if [sending_mta_ip] { + if [sending_mta_ip] != "" { mutate { merge => { "event.idm.read_only_udm.principal.ip" => "sending_mta_ip" } on_error => "tls_snd_ip_error" @@ -764,7 +778,7 @@ filter { } # -- security_result only on failures (result_type present). -- - if [result_type] { + if [result_type] != "" { mutate { replace => { "srt.summary" => "SMTP TLS report failure" @@ -780,85 +794,95 @@ filter { } # -- additional.fields. -- - if [organization_name] { + if [organization_name] != "" { mutate { replace => { "t_org.key" => "organization_name" "t_org.value.string_value" => "%{organization_name}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_org" } } } - if [begin_date] { + if [begin_date] != "" { mutate { replace => { "t_begin.key" => "begin_date" "t_begin.value.string_value" => "%{begin_date}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_begin" } } } - if [end_date] { + if [end_date] != "" { mutate { replace => { "t_end.key" => "end_date" "t_end.value.string_value" => "%{end_date}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_end" } } } - if [policy_domain] { + if [policy_domain] != "" { mutate { replace => { "t_pd.key" => "policy_domain" "t_pd.value.string_value" => "%{policy_domain}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_pd" } } } - if [policy_type] { + if [policy_type] != "" { mutate { replace => { "t_pt.key" => "policy_type" "t_pt.value.string_value" => "%{policy_type}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_pt" } } } - if [policy_strings] { + if [policy_strings] != "" { mutate { replace => { "t_ps.key" => "policy_strings" "t_ps.value.string_value" => "%{policy_strings}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_ps" } } } - if [mx_host_patterns] { + if [mx_host_patterns] != "" { mutate { replace => { "t_mx.key" => "mx_host_patterns" "t_mx.value.string_value" => "%{mx_host_patterns}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_mx" } } } - if [successful_session_count] { - mutate { - replace => { "t_ssc.key" => "successful_session_count" "t_ssc.value.string_value" => "%{successful_session_count}" } - merge => { "event.idm.read_only_udm.additional.fields" => "t_ssc" } - } + mutate { + replace => { "t_ssc.key" => "successful_session_count" "t_ssc.value.string_value" => "%{successful_session_count}" } } - if [failed_session_count] { - mutate { - replace => { "t_fsc.key" => "failed_session_count" "t_fsc.value.string_value" => "%{failed_session_count}" } - merge => { "event.idm.read_only_udm.additional.fields" => "t_fsc" } - } + mutate { + convert => { "t_ssc.value.string_value" => "uinteger" } + on_error => "ssc_nan" } - if [result_type] { + if ![ssc_nan] { + mutate { rename => { "t_ssc.value.string_value" => "t_ssc.value.number_value" } } + mutate { merge => { "event.idm.read_only_udm.additional.fields" => "t_ssc" } } + } + mutate { + replace => { "t_fsc.key" => "failed_session_count" "t_fsc.value.string_value" => "%{failed_session_count}" } + } + mutate { + convert => { "t_fsc.value.string_value" => "uinteger" } + on_error => "fsc_nan" + } + if ![fsc_nan] { + mutate { rename => { "t_fsc.value.string_value" => "t_fsc.value.number_value" } } + mutate { merge => { "event.idm.read_only_udm.additional.fields" => "t_fsc" } } + } + if [result_type] != "" { mutate { replace => { "t_rt.key" => "result_type" "t_rt.value.string_value" => "%{result_type}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_rt" } } } - if [failure_reason_code] { + if [failure_reason_code] != "" { mutate { replace => { "t_frc.key" => "failure_reason_code" "t_frc.value.string_value" => "%{failure_reason_code}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_frc" } } } - if [receiving_mx_hostname] { + if [receiving_mx_hostname] != "" { mutate { replace => { "t_rmh.key" => "receiving_mx_hostname" "t_rmh.value.string_value" => "%{receiving_mx_hostname}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_rmh" } } } - if [receiving_mx_helo] { + if [receiving_mx_helo] != "" { mutate { replace => { "t_rmhelo.key" => "receiving_mx_helo" "t_rmhelo.value.string_value" => "%{receiving_mx_helo}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_rmhelo" } } } - if [additional_info_uri] { + if [additional_info_uri] != "" { mutate { replace => { "t_aiu.key" => "additional_info_uri" "t_aiu.value.string_value" => "%{additional_info_uri}" } merge => { "event.idm.read_only_udm.additional.fields" => "t_aiu" } @@ -876,7 +900,7 @@ filter { "event.idm.read_only_udm.metadata.product_name" => "parsedmarc" } } - if [report_type] { + if [report_type] != "" { mutate { replace => { "event.idm.read_only_udm.metadata.product_event_type" => "%{report_type}"