Skip to content

Commit 0d8fc9b

Browse files
authored
Create serializer.lua
1 parent 1550197 commit 0d8fc9b

1 file changed

Lines changed: 325 additions & 0 deletions

File tree

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
-- palo_alto.lua beta
2+
-- Maps pre-parsed palo_alto.* fields to OCSF schema
3+
-- CSV parsing happens upstream; this script only does field mapping
4+
5+
------------------------------------------------------------------------
6+
-- Helper: set nested field by dot-path
7+
------------------------------------------------------------------------
8+
local function set_field(obj, path, value)
9+
if not obj or not path or path == "" or value == nil or value == "" then
10+
return
11+
end
12+
local current = obj
13+
local segments = {}
14+
for seg in path:gmatch("[^%.]+") do
15+
local name, idx = seg:match("^(.-)%[(%d+)%]$")
16+
if name and name ~= "" then
17+
segments[#segments + 1] = name
18+
segments[#segments + 1] = tonumber(idx)
19+
elseif name then
20+
segments[#segments + 1] = tonumber(idx)
21+
else
22+
segments[#segments + 1] = seg
23+
end
24+
end
25+
for i = 1, #segments - 1 do
26+
local s = segments[i]
27+
if current[s] == nil then current[s] = {} end
28+
current = current[s]
29+
end
30+
current[segments[#segments]] = value
31+
end
32+
33+
local function get_field(obj, path)
34+
if not obj or not path or path == "" then return nil end
35+
local current = obj
36+
for seg in path:gmatch("[^%.]+") do
37+
if type(current) ~= "table" then return nil end
38+
current = current[seg]
39+
if current == nil then return nil end
40+
end
41+
return current
42+
end
43+
44+
local function to_int(val)
45+
if val == nil or val == "" then return nil end
46+
return tonumber(val)
47+
end
48+
49+
------------------------------------------------------------------------
50+
-- Mapping tables: palo_alto field -> OCSF field
51+
------------------------------------------------------------------------
52+
53+
local THREAT_MAP = {
54+
-- Core fields
55+
{"receive_time", "time"},
56+
{"serial_number", "device.hw_info.serial_number"},
57+
{"log_type", "metadata.log_name"},
58+
{"log_subtype", "unmapped.sub_type"},
59+
{"generated_time", "metadata.original_time"},
60+
{"src_ip", "src_endpoint.ip"},
61+
{"dest_ip", "dst_endpoint.ip"},
62+
{"src_translated_ip", "src_endpoint.intermediate_ips[0]"},
63+
{"dest_translated_ip", "dst_endpoint.intermediate_ips[0]"},
64+
{"rule", "unmapped.rule_matched"},
65+
{"src_user", "actor.user.name"},
66+
{"dest_user", "unmapped.dst_user"},
67+
{"app", "app_name"},
68+
{"vsys", "unmapped.vsys"},
69+
{"src_zone", "unmapped.from_zone"},
70+
{"dest_zone", "unmapped.to_zone"},
71+
{"src_interface", "unmapped.inbound_if"},
72+
{"dest_interface", "unmapped.outbound_if"},
73+
{"log_forwarding_profile", "unmapped.log_action"},
74+
{"session_id", "actor.session.uid"},
75+
{"repeat_count", "unmapped.repeat_count"},
76+
{"src_port", "src_endpoint.port"},
77+
{"dest_port", "dst_endpoint.port"},
78+
{"src_translated_port", "unmapped.nat_src_port"},
79+
{"dest_translated_port", "unmapped.nat_dst_port"},
80+
{"session_flags", "unmapped.flags"},
81+
{"transport", "connection_info.protocol_name"},
82+
{"action", "unmapped.action"},
83+
{"misc", "unmapped.file"},
84+
{"threat", "unmapped.threat_id"},
85+
{"raw_category", "unmapped.url_category"},
86+
{"severity", "unmapped.severity"},
87+
{"direction", "unmapped.direction_of_attack"},
88+
{"sequence_number", "metadata.uid"},
89+
{"action_flags", "unmapped.action_flags"},
90+
{"src_location", "src_endpoint.location.region"},
91+
{"dest_location", "dst_endpoint.location.region"},
92+
{"content_type", "unmapped.contenttype"},
93+
{"pcap_id", "unmapped.pcap_id"},
94+
{"file_hash", "unmapped.file_digest"},
95+
{"cloud_address", "cloud.account_uid"},
96+
{"url_index", "unmapped.url_idx"},
97+
{"user_agent", "unmapped.user_agent"},
98+
{"file_type", "unmapped.file_type"},
99+
{"xff", "src_endpoint.intermediate_ips[1]"},
100+
{"referrer", "unmapped.referrer"},
101+
{"sender", "unmapped.sender_of_email"},
102+
{"subject", "unmapped.subject_of_email"},
103+
{"recipient", "unmapped.receipent_of_email"},
104+
{"report_id", "unmapped.report_id"},
105+
{"devicegroup_level1", "unmapped.dg_hier_level_1"},
106+
{"devicegroup_level2", "unmapped.dg_hier_level_2"},
107+
{"devicegroup_level3", "unmapped.dg_hier_level_3"},
108+
{"devicegroup_level4", "unmapped.dg_hier_level_4"},
109+
{"vsys_name", "unmapped.vsys_name"},
110+
{"dvc_name", "device.hostname"},
111+
{"http_method", "unmapped.http_method"},
112+
{"tunnel_id", "unmapped.tunnel_id"},
113+
{"tunnel_monitor_tag", "unmapped.tunnel_monitor_tag"},
114+
{"tunnel_session_id", "unmapped.tunnel_session_id"},
115+
{"tunnel_start_time", "unmapped.tunnel_start_time"},
116+
{"tunnel_type", "unmapped.tunnel_type"},
117+
{"threat_category", "unmapped.threat_category"},
118+
{"content_version", "unmapped.content_version"},
119+
}
120+
121+
local TRAFFIC_MAP = {
122+
{"receive_time", "time"},
123+
{"serial_number", "device.hw_info.serial_number"},
124+
{"log_type", "metadata.log_name"},
125+
{"log_subtype", "unmapped.sub_type"},
126+
{"generated_time", "metadata.original_time"},
127+
{"src_ip", "src_endpoint.ip"},
128+
{"dest_ip", "dst_endpoint.ip"},
129+
{"src_translated_ip", "src_endpoint.intermediate_ips[0]"},
130+
{"dest_translated_ip", "dst_endpoint.intermediate_ips[0]"},
131+
{"rule", "unmapped.rule_matched"},
132+
{"src_user", "actor.user.name"},
133+
{"dest_user", "unmapped.dst_user"},
134+
{"app", "app_name"},
135+
{"vsys", "unmapped.vsys"},
136+
{"src_zone", "unmapped.from_zone"},
137+
{"dest_zone", "unmapped.to_zone"},
138+
{"src_interface", "unmapped.inbound_if"},
139+
{"dest_interface", "unmapped.outbound_if"},
140+
{"log_forwarding_profile", "unmapped.log_action"},
141+
{"session_id", "actor.session.uid"},
142+
{"repeat_count", "unmapped.repeat_count"},
143+
{"src_port", "src_endpoint.port"},
144+
{"dest_port", "dst_endpoint.port"},
145+
{"src_translated_port", "unmapped.nat_src_port"},
146+
{"dest_translated_port", "unmapped.nat_dst_port"},
147+
{"session_flags", "unmapped.flags"},
148+
{"transport", "connection_info.protocol_name"},
149+
{"action", "unmapped.action"},
150+
{"dvc_name", "device.hostname"},
151+
{"src_location", "src_endpoint.location.region"},
152+
{"dest_location", "dst_endpoint.location.region"},
153+
}
154+
155+
local THREAT_CONSTANTS = {
156+
{"activity_name", "THREAT"},
157+
{"class_uid", 4001},
158+
{"activity_id", 99},
159+
{"category_uid", 4},
160+
{"type_uid", 400199},
161+
{"type_name", "Network Activity: Other"},
162+
{"class_name", "Network Activity"},
163+
{"category_name", "Network Activity"},
164+
{"metadata.version", "1.0.0-rc.3"},
165+
{"event.type", "THREAT"},
166+
{"status_id", 99},
167+
{"status", "Other"},
168+
{"connection_info.direction_id", 99},
169+
{"device.type_id", 99},
170+
{"dataSource.category", "security"},
171+
{"dataSource.name", "Palo Alto Networks Firewall"},
172+
{"dataSource.vendor", "Palo Alto Networks"},
173+
{"metadata.product.name", "Palo Alto Networks Firewall"},
174+
{"metadata.product.vendor_name", "Palo Alto Networks"},
175+
}
176+
177+
local TRAFFIC_CONSTANTS = {
178+
{"class_uid", 4001},
179+
{"category_uid", 4},
180+
{"severity_id", 0},
181+
{"class_name", "Network Activity"},
182+
{"category_name", "Network Activity"},
183+
{"metadata.version", "1.0.0-rc.3"},
184+
{"metadata.log_name", "TRAFFIC"},
185+
{"status_id", 99},
186+
{"status", "Other"},
187+
{"connection_info.direction_id", 99},
188+
{"device.type_id", 99},
189+
{"dataSource.category", "security"},
190+
{"dataSource.name", "Palo Alto Networks Firewall"},
191+
{"dataSource.vendor", "Palo Alto Networks"},
192+
{"metadata.product.name", "Palo Alto Networks Firewall"},
193+
{"metadata.product.vendor_name", "Palo Alto Networks"},
194+
}
195+
196+
local THREAT_COND_CONSTANTS = {
197+
{"severity_id", 1, "unmapped.severity", "informational"},
198+
{"severity_id", 2, "unmapped.severity", "low"},
199+
{"severity_id", 3, "unmapped.severity", "medium"},
200+
{"severity_id", 4, "unmapped.severity", "high"},
201+
{"severity_id", 5, "unmapped.severity", "critical"},
202+
{"status_id", 1, "unmapped.action", "allow"},
203+
{"status_id", 2, "unmapped.action", "deny"},
204+
{"status", "Success", "unmapped.action", "allow"},
205+
{"status", "Failure", "unmapped.action", "deny"},
206+
}
207+
208+
local TRAFFIC_COND_CONSTANTS = {
209+
{"activity_id", 1, "unmapped.sub_type", "start"},
210+
{"activity_id", 2, "unmapped.sub_type", "end"},
211+
{"activity_id", 4, "unmapped.sub_type", "drop"},
212+
{"activity_id", 5, "unmapped.sub_type", "deny"},
213+
{"activity_name", "Open", "unmapped.sub_type", "start"},
214+
{"activity_name", "Close", "unmapped.sub_type", "end"},
215+
{"activity_name", "Fail", "unmapped.sub_type", "drop"},
216+
{"activity_name", "Refuse", "unmapped.sub_type", "deny"},
217+
{"status_id", 1, "unmapped.action", "allow"},
218+
{"status_id", 2, "unmapped.action", "deny"},
219+
{"status", "Success", "unmapped.action", "allow"},
220+
{"status", "Failure", "unmapped.action", "deny"},
221+
}
222+
223+
local COPIES = {
224+
{"src_endpoint.ip", "observables[0].value"},
225+
{"dst_endpoint.ip", "observables[1].value"},
226+
{"device.hostname", "observables[2].value"},
227+
}
228+
229+
local OBSERVABLES_CONSTANTS = {
230+
{"observables[0].type_id", 2},
231+
{"observables[0].type", "IP Address"},
232+
{"observables[0].name", "src_endpoint.ip"},
233+
{"observables[1].type_id", 2},
234+
{"observables[1].type", "IP Address"},
235+
{"observables[1].name", "dst_endpoint.ip"},
236+
{"observables[2].type_id", 1},
237+
{"observables[2].type", "Hostname"},
238+
{"observables[2].name", "device.hostname"},
239+
}
240+
241+
------------------------------------------------------------------------
242+
-- Apply mapping from palo_alto.* to event
243+
------------------------------------------------------------------------
244+
local function apply_map(pa, event, map)
245+
for _, m in ipairs(map) do
246+
local src, dst = m[1], m[2]
247+
local val = pa[src]
248+
if val ~= nil and val ~= "" then
249+
set_field(event, dst, val)
250+
end
251+
end
252+
end
253+
254+
local function apply_constants(event, constants)
255+
for _, c in ipairs(constants) do
256+
set_field(event, c[1], c[2])
257+
end
258+
end
259+
260+
local function apply_cond_constants(event, cond_constants)
261+
for _, c in ipairs(cond_constants) do
262+
local actual = get_field(event, c[3])
263+
if actual == c[4] then
264+
set_field(event, c[1], c[2])
265+
end
266+
end
267+
end
268+
269+
local function apply_copies(event, copies)
270+
for _, c in ipairs(copies) do
271+
local val = get_field(event, c[1])
272+
if val then
273+
set_field(event, c[2], val)
274+
end
275+
end
276+
end
277+
278+
local function cast_int_fields(event, fields)
279+
for _, path in ipairs(fields) do
280+
local val = get_field(event, path)
281+
if val ~= nil and val ~= "" then
282+
set_field(event, path, to_int(val))
283+
end
284+
end
285+
end
286+
287+
------------------------------------------------------------------------
288+
-- Main processEvent
289+
------------------------------------------------------------------------
290+
function processEvent(event)
291+
local pa = event.palo_alto
292+
if not pa then
293+
return event
294+
end
295+
296+
local log_type = pa.log_type
297+
if not log_type then
298+
return event
299+
end
300+
301+
log_type = log_type:upper()
302+
303+
if log_type == "THREAT" then
304+
apply_map(pa, event, THREAT_MAP)
305+
apply_constants(event, THREAT_CONSTANTS)
306+
apply_constants(event, OBSERVABLES_CONSTANTS)
307+
apply_cond_constants(event, THREAT_COND_CONSTANTS)
308+
apply_copies(event, COPIES)
309+
cast_int_fields(event, {"src_endpoint.port", "dst_endpoint.port"})
310+
311+
elseif log_type == "TRAFFIC" then
312+
apply_map(pa, event, TRAFFIC_MAP)
313+
apply_constants(event, TRAFFIC_CONSTANTS)
314+
apply_constants(event, OBSERVABLES_CONSTANTS)
315+
apply_cond_constants(event, TRAFFIC_COND_CONSTANTS)
316+
apply_copies(event, COPIES)
317+
cast_int_fields(event, {"src_endpoint.port", "dst_endpoint.port"})
318+
319+
else
320+
set_field(event, "metadata.log_name", log_type)
321+
set_field(event, "unmapped.log_type", log_type)
322+
end
323+
324+
return event
325+
end

0 commit comments

Comments
 (0)