Record Descriptors ================== Most output generated by Dissect tools takes the form of :ref:`records `. You can create your own records by defining a record descriptor: .. code-block:: python from dissect.target.helpers.record import TargetRecordDescriptor FirewallRecord = TargetRecordDescriptor( "application/firewall", [ ("datetime", "ts"), ("net.ipaddress", "ip"), ("uint16", "port"), ("string", "protocol"), ], ) A record is composed of various :ref:`field types ` and their corresponding names. The :func:`~dissect.target.helpers.record.TargetRecordDescriptor` will add basic target information to complete your record. You can now generate records by filling the structure like this: .. code-block:: python firewall_record = FirewallRecord( ts=time.time(), ip="0.0.0.0", port=8080, protocol="tcp", _target=t ) Target related information like ``hostname`` and ``domain`` will be automatically added if you provide your target by setting the ``_target`` field. There are some default record descriptors extending :func:`~dissect.target.helpers.record.TargetRecordDescriptor` available for common scenarios: +--------------------------+--------------------+ | Record Descriptor | Additional Fields | +==========================+====================+ | ChildTargetRecord | * type (string) | | | * path (path) | +--------------------------+--------------------+ | UnixUserRecord | * name (string) | | | * passwd (string) | | | * uid (varint) | | | * gid (varint) | | | * gecos (string) | | | * home (path) | | | * shell (string) | | | * source (string) | +--------------------------+--------------------+ | WindowsUserRecord | * sid (string) | | | * name (string) | | | * home (path) | +--------------------------+--------------------+ If you create your own record descriptor, it is often practical to use existing ones as a basis. This helps to keep the naming uniform. To extend existing record descriptors use :func:`~dissect.target.helpers.record.create_extended_descriptor`. For instance, to create a record descriptor for browser downloads you could use: .. code-block:: python from dissect.target.helpers.record import create_extended_descriptor Descriptor = create_extended_descriptor([UserRecordDescriptorExtension]) DownloadRecord = Descriptor( "browser/download", [ ("uri", "url"), ("filesize", "size"), ] ) You can now combine data from several sources in one record: .. code-block:: python DownloadRecord( url="http://example.com/download.zip", size=123, _target=t, _user=u ) Note that `source fields` (``_target``, ``_user``) from the extended record descriptors are provided using keywords starting with an underscore. It is also possible to use multiple existing record descriptor extensions as the basis of your new descriptor: .. code-block:: python from dissect.target.helpers.record import create_extended_descriptor UserRegistryRecordDescriptor = create_extended_descriptor( [ RegistryRecordDescriptorExtension, UserRecordDescriptorExtension, ] ) The following record descriptor extensions are available: +------------------------------------+--------------------+--------------+ | Record Descriptor Extension | Fields | Source Field | +====================================+====================+==============+ | RegistryRecordDescriptorExtension | * regf_hive_path | _key | | | * regf_key_path | | +------------------------------------+--------------------+--------------+ | TargetRecordDescriptorExtension | * domain | _target | | | * hostname | | +------------------------------------+--------------------+--------------+ | UserRecordDescriptorExtension | * username | _user | | | * user_id | | | | * user_group | | | | * user_home | | +------------------------------------+--------------------+--------------+ GroupedRecord ------------- A :class:`~flow.record.base.GroupedRecord` holds multiple records and offers a flat view of the records. Suppose you wish to record an event system that contains events with triggers and events with actions. You could use a :class:`~flow.record.base.GroupedRecord` to compose records reflecting these combinations: .. code-block:: python from flow.record import GroupedRecord if action: yield GroupedRecord("event/grouped", [event, action]) elif trigger: yield GroupedRecord("event/grouped", [event, trigger]) If two records have the same fieldname, the first one will prevail. .. warning:: Note that, this record type cannot be used to nest records. Nesting records is not possible. DynamicDescriptor ----------------- The :func:`~dissect.target.helpers.record.DynamicDescriptor` function returns a plain record descriptor with the provided types. This function can be used if your plugin creates its own record descriptor dynamically but you still wish to provide certain field types through the export decorator. Let's say you create a dynamic descriptor with fields ``fields``: .. code-block:: python yield TargetRecordDescriptor("sql/table", fields)( _target=self.target, **values, ) In this case you might want to communicate that ``fields`` at least contains a digest type: .. code-block:: python @export(record=DynamicDescriptor(["digest"])) This allows other tools that are interested in records having specific field types to check if your plugin function provides this.