@@ -283,14 +283,46 @@ 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 ) as exc :
298+ logger .debug (
299+ "Unable to format %s value %r as timestamp in MISP export: %s" ,
300+ field ,
301+ value ,
302+ exc ,
303+ )
304+
286305 event = payload .get ("Event" , {})
287306 _strip_ids (event )
288307 for attribute in event .get ("Attribute" , []):
289308 _strip_ids (attribute )
309+ _format_seen (attribute )
290310 for misp_object in event .get ("Object" , []):
291311 _strip_ids (misp_object )
312+ _format_seen (misp_object )
292313 for attribute in misp_object .get ("Attribute" , []):
293314 _strip_ids (attribute )
315+ _format_seen (attribute )
316+
317+ # Drop keys with null values so the exported MISP JSON stays compact.
318+ def _drop_nulls (obj ):
319+ if isinstance (obj , dict ):
320+ return {k : _drop_nulls (v ) for k , v in obj .items () if v is not None }
321+ if isinstance (obj , list ):
322+ return [_drop_nulls (v ) for v in obj ]
323+ return obj
324+
325+ payload = _drop_nulls (payload )
294326
295327 content = json .dumps (payload , default = str , indent = 2 ).encode ("utf-8" )
296328 return content , "json" , "application/json" , len (attributes )
0 commit comments