11 local my_host = nil |
11 local my_host = nil |
12 |
12 |
13 -- // Util and Functions // |
13 -- // Util and Functions // |
14 |
14 |
15 local function ft_str() |
15 local function ft_str() |
16 local d = os_date("%FT%T%z"):gsub("^(.*)(%+%d+)", function(dt, z) |
16 local d = os_date("%FT%T%z"):gsub("^(.*)(%+%d+)", function(dt, z) |
17 if z == "+0000" then return dt.."Z" else return dt..z end |
17 if z == "+0000" then return dt.."Z" else return dt..z end |
18 end) |
18 end) |
19 return d |
19 return d |
20 end |
20 end |
21 |
21 |
22 local function get_incident_layout(i_type) |
22 local function get_incident_layout(i_type) |
23 local layout = { |
23 local layout = { |
24 title = (i_type == "report" and "Incident report form") or (i_type == "request" and "Request for assistance with incident form"), |
24 title = (i_type == "report" and "Incident report form") or (i_type == "request" and "Request for assistance with incident form"), |
25 instructions = "Started/Ended Time, Contacts, Sources and Targets of the attack are mandatory. See RFC 5070 for further format instructions.", |
25 instructions = "Started/Ended Time, Contacts, Sources and Targets of the attack are mandatory. See RFC 5070 for further format instructions.", |
26 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/commands" }, |
26 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/commands" }, |
27 |
27 |
28 { name = "name", type = "hidden", value = my_host }, |
28 { name = "name", type = "hidden", value = my_host }, |
29 { name = "entity", type ="text-single", label = "Remote entity to query" }, |
29 { name = "entity", type ="text-single", label = "Remote entity to query" }, |
30 { name = "started", type = "text-single", label = "Incident Start Time" }, |
30 { name = "started", type = "text-single", label = "Incident Start Time" }, |
31 { name = "ended", type = "text-single", label = "Incident Ended Time" }, |
31 { name = "ended", type = "text-single", label = "Incident Ended Time" }, |
32 { name = "reported", type = "hidden", value = ft_str() }, |
32 { name = "reported", type = "hidden", value = ft_str() }, |
33 { name = "description", type = "text-single", label = "Description", |
33 { name = "description", type = "text-single", label = "Description", |
34 desc = "Description syntax is: <lang (in xml:lang format)> <short description>" }, |
34 desc = "Description syntax is: <lang (in xml:lang format)> <short description>" }, |
35 { name = "contacts", type = "text-multi", label = "Contacts", |
35 { name = "contacts", type = "text-multi", label = "Contacts", |
36 desc = "Contacts entries format is: <address> <type> <role> - separated by new lines" }, |
36 desc = "Contacts entries format is: <address> <type> <role> - separated by new lines" }, |
37 { name = "related", type = "text-multi", label = "Related Incidents", |
37 { name = "related", type = "text-multi", label = "Related Incidents", |
38 desc = "Related incidents entries format is: <CSIRT's FQDN> <Incident ID> - separated by new lines" }, |
38 desc = "Related incidents entries format is: <CSIRT's FQDN> <Incident ID> - separated by new lines" }, |
39 { name = "impact", type = "text-single", label = "Impact Assessment", |
39 { name = "impact", type = "text-single", label = "Impact Assessment", |
40 desc = "Impact assessment format is: <severity> <completion> <type>" }, |
40 desc = "Impact assessment format is: <severity> <completion> <type>" }, |
41 { name = "sources", type = "text-multi", label = "Attack Sources", |
41 { name = "sources", type = "text-multi", label = "Attack Sources", |
42 desc = "Attack sources format is: <address> <category> <count> <count-type>" }, |
42 desc = "Attack sources format is: <address> <category> <count> <count-type>" }, |
43 { name = "targets", type = "text-multi", label = "Attack Targets", |
43 { name = "targets", type = "text-multi", label = "Attack Targets", |
44 desc = "Attack target format is: <address> <category> <noderole>" } |
44 desc = "Attack target format is: <address> <category> <noderole>" } |
45 } |
45 } |
46 |
46 |
47 if i_type == "request" then |
47 if i_type == "request" then |
48 table.insert(layout, { |
48 table.insert(layout, { |
49 name = "expectation", |
49 name = "expectation", |
50 type = "list-single", |
50 type = "list-single", |
51 label = "Expected action from remote entity", |
51 label = "Expected action from remote entity", |
52 value = { |
52 value = { |
53 { value = "nothing", label = "No action" }, |
53 { value = "nothing", label = "No action" }, |
104 if contact.email then insert_fixed(layout, "--> E-Mail: "..contact.email) end |
104 if contact.email then insert_fixed(layout, "--> E-Mail: "..contact.email) end |
105 if contact.telephone then insert_fixed(layout, "--> Telephone: "..contact.telephone) end |
105 if contact.telephone then insert_fixed(layout, "--> Telephone: "..contact.telephone) end |
106 if contact.postaladdr then insert_fixed(layout, "--> Postal Address: "..contact.postaladdr) end |
106 if contact.postaladdr then insert_fixed(layout, "--> Postal Address: "..contact.postaladdr) end |
107 end |
107 end |
108 |
108 |
109 insert_fixed(layout, "Related Activity --") |
109 insert_fixed(layout, "Related Activity --") |
110 for _, related in ipairs(incident.data.related) do |
110 for _, related in ipairs(incident.data.related) do |
111 insert_fixed(layout, string.format("Name: %s ID: %s", related.name, related.text)) |
111 insert_fixed(layout, string.format("Name: %s ID: %s", related.name, related.text)) |
112 end |
112 end |
113 |
113 |
114 insert_fixed(layout, "Assessment --") |
114 insert_fixed(layout, "Assessment --") |
224 object.related[#object.related + 1] = { text = t:get_text(), name = tag.attr.name } |
224 object.related[#object.related + 1] = { text = t:get_text(), name = tag.attr.name } |
225 end |
225 end |
226 end |
226 end |
227 elseif tag.name == "Assessment" then |
227 elseif tag.name == "Assessment" then |
228 local impact = tag:get_child("Impact") |
228 local impact = tag:get_child("Impact") |
229 object.assessment = { lang = impact.attr.lang, severity = impact.attr.severity, completion = impact.attr.completion, type = impact.attr.type } |
229 object.assessment = { lang = impact.attr.lang, severity = impact.attr.severity, completion = impact.attr.completion, type = impact.attr.type } |
230 elseif tag.name == "EventData" then |
230 elseif tag.name == "EventData" then |
231 local source = tag:get_child("Flow").tags[1] |
231 local source = tag:get_child("Flow").tags[1] |
232 local target = tag:get_child("Flow").tags[2] |
232 local target = tag:get_child("Flow").tags[2] |
233 local expectation = tag:get_child("Flow").tags[3] |
233 local expectation = tag:get_child("Flow").tags[3] |
234 object.event_data = { sources = {}, targets = {} } |
234 object.event_data = { sources = {}, targets = {} } |
242 end |
242 end |
243 for _, entry in ipairs(target.tags) do |
243 for _, entry in ipairs(target.tags) do |
244 local noderole = { cat = entry:get_child("NodeRole").attr.category, ext = entry:get_child("NodeRole").attr["ext-category"] } |
244 local noderole = { cat = entry:get_child("NodeRole").attr.category, ext = entry:get_child("NodeRole").attr["ext-category"] } |
245 local current = #object.event_data.targets + 1 |
245 local current = #object.event_data.targets + 1 |
246 object.event_data.targets[current] = { addresses = {}, noderole = noderole } |
246 object.event_data.targets[current] = { addresses = {}, noderole = noderole } |
247 for _, tag in ipairs(entry.tags) do |
247 for _, tag in ipairs(entry.tags) do |
248 object.event_data.targets[current].addresses[#object.event_data.targets[current].addresses + 1] = { text = tag:get_text(), cat = tag.attr.category, ext = tag.attr["ext-category"] } |
248 object.event_data.targets[current].addresses[#object.event_data.targets[current].addresses + 1] = { text = tag:get_text(), cat = tag.attr.category, ext = tag.attr["ext-category"] } |
249 end |
249 end |
250 end |
250 end |
251 if expectation then |
251 if expectation then |
252 object.event_data.expectation = { |
252 object.event_data.expectation = { |
253 action = expectation.attr.action, |
253 action = expectation.attr.action, |
254 desc = expectation:get_child("Description") and expectation:get_child("Description"):get_text() |
254 desc = expectation:get_child("Description") and expectation:get_child("Description"):get_text() |
255 } |
255 } |
256 end |
256 end |
257 elseif tag.name == "History" then |
257 elseif tag.name == "History" then |
258 object.history = {} |
258 object.history = {} |
259 for _, t in ipairs(tag.tags) do |
259 for _, t in ipairs(tag.tags) do |
260 object.history[#object.history + 1] = { |
260 object.history[#object.history + 1] = { |
293 :tag("IncidentID", { name = object.id.name }):text(object.id.text):up() |
293 :tag("IncidentID", { name = object.id.name }):text(object.id.text):up() |
294 :tag("StartTime"):text(object.start_time):up() |
294 :tag("StartTime"):text(object.start_time):up() |
295 :tag("EndTime"):text(object.end_time):up() |
295 :tag("EndTime"):text(object.end_time):up() |
296 :tag("ReportTime"):text(object.report_time):up() |
296 :tag("ReportTime"):text(object.report_time):up() |
297 :tag("Description", { ["xml:lang"] = object.desc.lang }):text(object.desc.text):up():up(); |
297 :tag("Description", { ["xml:lang"] = object.desc.lang }):text(object.desc.text):up():up(); |
298 |
298 |
299 local incident = stanza:get_child(s_type, xmlns_inc):get_child("Incident", xmlns_iodef) |
299 local incident = stanza:get_child(s_type, xmlns_inc):get_child("Incident", xmlns_iodef) |
300 |
300 |
301 for _, contact in ipairs(object.contacts) do |
301 for _, contact in ipairs(object.contacts) do |
302 incident:tag("Contact", { role = (contact.ext_role and "ext-role") or contact.role, |
302 incident:tag("Contact", { role = (contact.ext_role and "ext-role") or contact.role, |
303 ["ext-role"] = (contact.ext_role and contact.role) or nil, |
303 ["ext-role"] = (contact.ext_role and contact.role) or nil, |
304 type = (contact.ext_type and "ext-type") or contact.type, |
304 type = (contact.ext_type and "ext-type") or contact.type, |
306 :tag("Email"):text(contact.email):up() |
306 :tag("Email"):text(contact.email):up() |
307 :tag("Telephone"):text(contact.telephone):up() |
307 :tag("Telephone"):text(contact.telephone):up() |
308 :tag("PostalAddress"):text(contact.postaladdr):up() |
308 :tag("PostalAddress"):text(contact.postaladdr):up() |
309 :tag("AdditionalData") |
309 :tag("AdditionalData") |
310 :tag("jid", { xmlns = contact.xmlns }):text(contact.jid):up():up():up() |
310 :tag("jid", { xmlns = contact.xmlns }):text(contact.jid):up():up():up() |
311 |
311 |
312 end |
312 end |
313 |
313 |
314 incident:tag("RelatedActivity"):up(); |
314 incident:tag("RelatedActivity"):up(); |
315 |
315 |
316 for _, related in ipairs(object.related) do |
316 for _, related in ipairs(object.related) do |
317 incident:get_child("RelatedActivity") |
317 incident:get_child("RelatedActivity") |
318 :tag("IncidentID", { name = related.name }):text(related.text):up(); |
318 :tag("IncidentID", { name = related.name }):text(related.text):up(); |
319 end |
319 end |
320 |
320 |
321 incident:tag("Assessment") |
321 incident:tag("Assessment") |
322 :tag("Impact", { |
322 :tag("Impact", { |
323 lang = object.assessment.lang, |
323 lang = object.assessment.lang, |
324 severity = object.assessment.severity, |
324 severity = object.assessment.severity, |
325 completion = object.assessment.completion, |
325 completion = object.assessment.completion, |
326 type = object.assessment.type |
326 type = object.assessment.type |
327 }):up():up(); |
327 }):up():up(); |
360 end |
360 end |
361 end |
361 end |
362 |
362 |
363 if object.history then |
363 if object.history then |
364 local history = incident:tag("History"):up(); |
364 local history = incident:tag("History"):up(); |
365 |
365 |
366 for _, item in ipairs(object.history) do |
366 for _, item in ipairs(object.history) do |
367 history:tag("HistoryItem", { action = item.action }) |
367 history:tag("HistoryItem", { action = item.action }) |
368 :tag("DateTime"):text(item.date):up() |
368 :tag("DateTime"):text(item.date):up() |
369 :tag("Description"):text(item.desc):up():up(); |
369 :tag("Description"):text(item.desc):up():up(); |
370 end |
370 end |
371 end |
371 end |
372 |
372 |
373 -- Sanitize contact empty tags |
373 -- Sanitize contact empty tags |
374 for _, tag in ipairs(incident) do |
374 for _, tag in ipairs(incident) do |
375 if tag.name == "Contact" then |
375 if tag.name == "Contact" then |
376 for i, check in ipairs(tag) do |
376 for i, check in ipairs(tag) do |
377 if (check.name == "Email" or check.name == "PostalAddress" or check.name == "Telephone") and |
377 if (check.name == "Email" or check.name == "PostalAddress" or check.name == "Telephone") and |
378 not check:get_text() then |
378 not check:get_text() then |
379 table.remove(tag, i) |
379 table.remove(tag, i) |
380 end |
380 end |
381 end |
381 end |
382 end |
382 end |
383 end |
383 end |
384 |
384 |
385 if s_type == "request" then stanza.attr.type = "get" |
385 if s_type == "request" then stanza.attr.type = "get" |
386 elseif s_type == "response" then stanza.attr.type = "set" |
386 elseif s_type == "response" then stanza.attr.type = "set" |
387 else stanza.attr.type = "set" end |
387 else stanza.attr.type = "set" end |
388 |
388 |
389 return stanza |
389 return stanza |
390 end |
390 end |
391 end |
391 end |
392 |
392 |
393 |
393 |
394 _M = {} -- wraps methods into the library. |
394 _M = {} -- wraps methods into the library. |
395 _M.ft_str = ft_str |
395 _M.ft_str = ft_str |
396 _M.get_incident_layout = get_incident_layout |
396 _M.get_incident_layout = get_incident_layout |