@@ -283,14 +283,41 @@ def _strip_ids(obj: dict) -> None:
283283 obj .pop ("uuid" , None )
284284 obj .pop ("id" , None )
285285
286+ # first_seen/last_seen are stored as epoch seconds; MISP serializes them as
287+ # microsecond-precision ISO-8601 strings (e.g. "2026-06-19T00:00:00.000000+00:00").
288+ def _format_seen (obj : dict ) -> None :
289+ for field in ("first_seen" , "last_seen" ):
290+ value = obj .get (field )
291+ if value is None :
292+ continue
293+ try :
294+ obj [field ] = datetime .fromtimestamp (
295+ int (value ), tz = timezone .utc
296+ ).isoformat (timespec = "microseconds" )
297+ except (ValueError , TypeError , OverflowError ):
298+ pass
299+
286300 event = payload .get ("Event" , {})
287301 _strip_ids (event )
288302 for attribute in event .get ("Attribute" , []):
289303 _strip_ids (attribute )
304+ _format_seen (attribute )
290305 for misp_object in event .get ("Object" , []):
291306 _strip_ids (misp_object )
307+ _format_seen (misp_object )
292308 for attribute in misp_object .get ("Attribute" , []):
293309 _strip_ids (attribute )
310+ _format_seen (attribute )
311+
312+ # Drop keys with null values so the exported MISP JSON stays compact.
313+ def _drop_nulls (obj ):
314+ if isinstance (obj , dict ):
315+ return {k : _drop_nulls (v ) for k , v in obj .items () if v is not None }
316+ if isinstance (obj , list ):
317+ return [_drop_nulls (v ) for v in obj ]
318+ return obj
319+
320+ payload = _drop_nulls (payload )
294321
295322 content = json .dumps (payload , default = str , indent = 2 ).encode ("utf-8" )
296323 return content , "json" , "application/json" , len (attributes )
0 commit comments