Hey everyone,
I've started working on a custom parser for NetSuite, since it's a major product my company currently uses. Initially, I was having some success extracting the necessary data from my logs. However, when I switched to a different raw log, I began running into issues.
I initialized all the variables I could identify from the raw logs, assuming that if a field didn’t exist, the parser would simply skip it and continue processing the rest. Unfortunately, that doesn’t seem to be happening.
Right now, I’m just dumping these fields into the additional_fields section. I know that’s not best practice, but since I’m the only one using the SIEM at the moment and enrichment for this log type isn’t a high priority, it’s working for now.
Here is my current parser logic (I removed some to make this post shorter):
filter {
# This parser is just working for the incident we are currently having with GSM - it will need to be changed to include other fields
mutate {
replace => {
"udm_event.idm.read_only_udm.metadata.vendor_name" => "Net Suite"
"udm_event.idm.read_only_udm.metadata.event_type" => "GENERIC_EVENT"
"title" => ""
"type" => ""
"user" => ""
"scripttype" => ""
"detail.clientReferenceInformation.code" => ""
"detail.clientReferenceInformation.partner.solutionId" => ""
#"detail.processingInformation.authorizationOptions.initiator.credentialStoredOnFile" => ""
#"detail.processingInformation.authorizationOptions.ignoreAvsResult" => ""
#"detail.processingInformation.authorizationOptions.ignoreCvResult" => ""
"detail.processingInformation.commerceIndicator" => ""
"detail.paymentInformation.card.number" => ""
"detail.paymentInformation.card.expirationMonth" => ""
"detail.paymentInformation.card.expirationYear" => ""
"detail.paymentInformation.card.securityCode" => ""
"detail.paymentInformation.card.type" => ""
"detail.orderInformation.amountDetails.totalAmount" => ""
"detail.orderInformation.amountDetails.currency" => ""
"detail.orderInformation.amountDetails.discountAmount" => ""
"detail.orderInformation.amountDetails.taxAmount" => ""
"detail.orderInformation.billTo.firstName" => ""
"detail.orderInformation.billTo.lastName" => ""
"detail.orderInformation.billTo.address1" => ""
"detail.orderInformation.billTo.locality" => ""
"detail.orderInformation.billTo.administrativeArea" => ""
"detail.orderInformation.billTo.postalCode" => ""
"detail.orderInformation.billTo.country" => ""
"detail.orderInformation.billTo.email" => ""
"detail.orderInformation.billTo.phoneNumber" => ""
"detail.orderInformation.shipTo.firstName" => ""
"detail.orderInformation.shipTo.lastName" => ""
"detail.orderInformation.shipTo.address1" => ""
"detail.orderInformation.shipTo.locality" => ""
"detail.orderInformation.shipTo.administrativeArea" => ""
"detail.orderInformation.shipTo.postalCode" => ""
"detail.orderInformation.shipTo.country" => ""
"detail.paymentInsightsInformation.responseInsights.category" => ""
}
}
# Parse JSON
json {
source => "message"
on_error => "not_json"
}
statedump{label=TheStart}
# Handle the title field
if ltitle] != "" {
mutate {
replace => {
"title_field.key" => "Title"
"title_field.value.string_value" => "%{title}"
}
}
mutate {
merge => {
"udm_event.idm.read_only_udm.additional.fields" => "title_field"
}
}
}
# Handle the type field
if ltype] != "" {
mutate {
replace => {
"type_field.key" => "Type"
"type_field.value.string_value" => "%{type}"
}
}
mutate {
merge => {
"udm_event.idm.read_only_udm.additional.fields" => "type_field"
}
}
}
# Handle the user field
if luser] != "" {
mutate {
replace => {
"user_field.key" => "User"
"user_field.value.string_value" => "%{user}"
}
}
mutate {
merge => {
"udm_event.idm.read_only_udm.additional.fields" => "user_field"
}
}
}
# Handle the scripttype field
if lscripttype] != "" {
mutate {
replace => {
"scripttype_field.key" => "ScriptType"
"scripttype_field.value.string_value" => "%{scripttype}"
}
}
mutate {
merge => {
"udm_event.idm.read_only_udm.additional.fields" => "scripttype_field"
}
}
}
# Handle client reference information code
if ddetail]
clientReferenceInformation]ccode] != "" {
mutate {
replace => {
"clientrefcode_field.key" => "ClientReferenceCode"
"clientrefcode_field.value.string_value" => "%{detail.clientReferenceInformation.code}"
}
}
mutate {
merge => {
"udm_event.idm.read_only_udm.additional.fields" => "clientrefcode_field"
}
}
}
# Handle client solution ID
if Idetail]
clientReferenceInformation]cpartner]tsolutionId] != "" {
mutate {
replace => {
"solutionid_field.key" => "ClientSolutionId"
"solutionid_field.value.string_value" => "%{detail.clientReferenceInformation.partner.solutionId}"
}
}
mutate {
merge => {
"udm_event.idm.read_only_udm.additional.fields" => "solutionid_field"
}
}
}
statedump{label=TheEnd}
mutate {
merge => {
"@output" => "udm_event"
}
}
This is the error that I am running into currently:
generic::unknown: pipeline.ParseLogEntry failed: LOG_PARSING_CBN_ERROR: "generic::invalid_argument: pipeline failed: filter conditional (2) failed: failed to evaluate expression: generic::invalid_argument: \\"detail.clientReferenceInformation\\" not found in state data"
And here is the raw log from Netsuite that I am attempting to parse:
{
"title": " Basic AUTHORIZATION Response Code",
"type": "Audit",
"date": "6/17/2025",
"time": "11:00 pm",
"user": "889990397 USERNAME",
"scripttype": "Payment Processing Plug-in",
"detail": 201,
"name.script": "Cybersource for NetSuite"
}