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 [title] != "" {
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 [type] != "" {
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 [user] != "" {
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 [scripttype] != "" {
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 [detail][clientReferenceInformation][code] != "" {
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 [detail][clientReferenceInformation][partner][solutionId] != "" {
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"
}