:py:mod:`dissect.target.plugin` =============================== .. py:module:: dissect.target.plugin .. autoapi-nested-parse:: Dissect plugin system. See dissect/target/plugins/general/example.py for an example plugin. Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: dissect.target.plugin.OperatingSystem dissect.target.plugin.Plugin dissect.target.plugin.OSPlugin dissect.target.plugin.ChildTargetPlugin dissect.target.plugin.NamespacePlugin dissect.target.plugin.InternalPlugin dissect.target.plugin.PluginFunction Functions ~~~~~~~~~ .. autoapisummary:: :nosignatures: dissect.target.plugin.export dissect.target.plugin.get_nonprivate_attribute_names dissect.target.plugin.get_nonprivate_attributes dissect.target.plugin.get_nonprivate_methods dissect.target.plugin.get_descriptors_on_nonprivate_methods dissect.target.plugin.register dissect.target.plugin.internal dissect.target.plugin.arg dissect.target.plugin.plugins dissect.target.plugin.os_plugins dissect.target.plugin.child_plugins dissect.target.plugin.lookup dissect.target.plugin.get_plugins_by_func_name dissect.target.plugin.get_plugins_by_namespace dissect.target.plugin.load dissect.target.plugin.failed dissect.target.plugin.save_plugin_import_failure dissect.target.plugin.find_py_files dissect.target.plugin.load_module_from_name dissect.target.plugin.generate dissect.target.plugin.load_module_from_file dissect.target.plugin.load_modules_from_paths dissect.target.plugin.get_external_module_paths dissect.target.plugin.environment_variable_paths dissect.target.plugin.plugin_function_index dissect.target.plugin.find_plugin_functions Attributes ~~~~~~~~~~ .. autoapisummary:: dissect.target.plugin.GENERATED dissect.target.plugin.PluginDescriptor dissect.target.plugin.MODULE_PATH dissect.target.plugin.OUTPUTS dissect.target.plugin.log .. py:data:: GENERATED :value: True .. py:data:: PluginDescriptor A dictionary type, for what the plugin descriptor looks like. .. py:data:: MODULE_PATH :value: 'dissect.target.plugins' The base module path to the in-tree plugins. .. py:data:: OUTPUTS :value: ('default', 'record', 'yield', 'none') The different output types supported by ``@export``. .. py:data:: log .. py:class:: OperatingSystem Bases: :py:obj:`dissect.target.helpers.utils.StrEnum` Sortable and serializible string-based enum .. py:attribute:: LINUX :value: 'linux' .. py:attribute:: WINDOWS :value: 'windows' .. py:attribute:: ESXI :value: 'esxi' .. py:attribute:: BSD :value: 'bsd' .. py:attribute:: OSX :value: 'osx' .. py:attribute:: UNIX :value: 'unix' .. py:attribute:: ANDROID :value: 'android' .. py:attribute:: VYOS :value: 'vyos' .. py:attribute:: IOS :value: 'ios' .. py:attribute:: FORTIOS :value: 'fortios' .. py:attribute:: CITRIX :value: 'citrix-netscaler' .. py:function:: export(*args, **kwargs) -> Callable Decorator to be used on Plugin functions that should be exported. Supported keyword arguments: property (bool): Whether this export should be regarded as a property. Properties are implicitly cached. cache (bool): Whether the result of this function should be cached. record (RecordDescriptor): The :class:`flow.record.RecordDescriptor` for the records that this function yields. If the records are dynamically made, use DynamicRecord instead. output (str): The output type of this function. Can be one of: - default: Single return value - record: Yields records. Implicit when record argument is given. - yield: Yields printable values. - none: No return value. Plugin is responsible for output formatting and should return ``None``. The ``export`` decorator adds some additional private attributes to an exported method or property: - ``__output__``: The output type to expect for this function, this is the same as ``output``. - ``__record__``: The type of record to expect, this value is the same as ``record``. - ``__exported__``: set to ``True`` to indicate the method or property is exported. :raises ValueError: if there was an invalid output type. :returns: An exported function from a plugin. .. py:function:: get_nonprivate_attribute_names(cls: Type[Plugin]) -> list[str] Retrieve all attributes that do not start with ``_``. .. py:function:: get_nonprivate_attributes(cls: Type[Plugin]) -> list[Any] Retrieve all public attributes of a :class:`Plugin`. .. py:function:: get_nonprivate_methods(cls: Type[Plugin]) -> list[Callable] Retrieve all public methods of a :class:`Plugin`. .. py:function:: get_descriptors_on_nonprivate_methods(cls: Type[Plugin]) -> list[flow.record.RecordDescriptor] Return record descriptors set on nonprivate methods in `cls` class. .. py:class:: Plugin(target: dissect.target.Target) Base class for plugins. Plugins can optionally be namespaced by specifying the ``__namespace__`` class attribute. Namespacing results in your plugin needing to be prefixed with this namespace when being called. For example, if your plugin has specified ``test`` as namespace and a function called ``example``, you must call your plugin with ``test.example``:: A ``Plugin`` class has the following private class attributes: - ``__namespace__`` - ``__record_descriptors__`` With the following three being assigned in :func:`register`: - ``__plugin__`` - ``__functions__`` - ``__exports__`` Additionally, the methods and attributes of :class:`Plugin` receive more private attributes by using decorators. The :func:`export` decorator adds the following private attributes - ``__exported__`` - ``__output__``: Set with the :func:`export` decorator. - ``__record__``: Set with the :func:`export` decorator. The :func:`internal` decorator and :class:`InternalPlugin` set the ``__internal__`` attribute. Finally. :func:`args` decorator sets the ``__args__`` attribute. :param target: The :class:`~dissect.target.target.Target` object to load the plugin for. .. py:attribute:: __namespace__ :type: str Defines the plugin namespace. .. py:attribute:: __record_descriptors__ :type: list[flow.record.RecordDescriptor] Defines a list of :class:`~flow.record.RecordDescriptor` of the exported plugin functions. .. py:attribute:: __register__ :type: bool :value: True Determines whether this plugin will be registered. .. py:attribute:: __findable__ :type: bool :value: True Determines whether this plugin will be revealed when using search patterns. Some (meta)-plugins are not very suitable for wild cards on CLI or plugin searches, because they will produce duplicate records or results. For instance a plugin that offers the same functions as subplugins will produce redundant results when used with a wild card (browser.* -> browser.history + browser.*.history). .. py:method:: __init_subclass__(**kwargs) :classmethod: .. py:method:: is_compatible() -> bool Perform a compatibility check with the target. .. py:method:: check_compatible() -> None :abstractmethod: Perform a compatibility check with the target. This function should return ``None`` if the plugin is compatible with the current target (``self.target``). For example, check if a certain file exists. Otherwise it should raise an ``UnsupportedPluginError``. :raises UnsupportedPluginError: If the plugin could not be loaded. .. py:method:: get_all_records() -> Iterator[flow.record.Record] Return the records of all exported methods. :raises PluginError: If the subclass is not a namespace plugin. .. py:method:: __call__(*args, **kwargs) A shortcut to :func:`get_all_records`. :raises PluginError: If the subclass is not a namespace plugin. .. py:class:: OSPlugin(target: dissect.target.Target) Bases: :py:obj:`Plugin` Base class for OS plugins. This provides a base class for certain common functions of OS's, which each OS plugin has to implement separately. For example, it provides an interface for retrieving the hostname and users of a target. All derived classes MUST implement ALL the classmethods and exported methods with the same ``@classmethod`` or ``@export(...)`` annotation. .. py:method:: __init_subclass__(**kwargs) :classmethod: .. py:method:: check_compatible() -> bool OSPlugin's use a different compatibility check, override the one from the :class:`Plugin` class. :returns: This function always returns ``True``. .. py:method:: detect(fs: dissect.target.filesystem.Filesystem) -> Optional[dissect.target.filesystem.Filesystem] :classmethod: :abstractmethod: Provide detection of this OSPlugin on a given filesystem. :param fs: :class:`~dissect.target.filesystem.Filesystem` to detect the OS on. :returns: The root filesystem / sysvol when found. .. py:method:: create(target: dissect.target.Target, sysvol: dissect.target.filesystem.Filesystem) -> OSPlugin :classmethod: :abstractmethod: Initiate this OSPlugin with the given target and detected filesystem. :param target: The :class:`~dissect.target.target.Target` object. :param sysvol: The filesystem that was detected in the ``detect()`` function. :returns: An instantiated version of the OSPlugin. .. py:method:: hostname() -> Optional[str] :abstractmethod: Return the target's hostname. :returns: The hostname as string. .. py:method:: ips() -> list[str] :abstractmethod: Return the IP addresses configured in the target. :returns: The IPs as list. .. py:method:: version() -> Optional[str] :abstractmethod: Return the target's OS version. :returns: The OS version as string. .. py:method:: users() -> list[flow.record.Record] :abstractmethod: Return the users available in the target. :returns: A list of user records. .. py:method:: os() -> str :abstractmethod: Return a slug of the target's OS name. :returns: A slug of the OS name, e.g. 'windows' or 'linux'. .. py:method:: architecture() -> Optional[str] :abstractmethod: Return a slug of the target's OS architecture. :returns: A slug of the OS architecture, e.g. 'x86_32-unix', 'MIPS-linux' or 'AMD64-win32', or 'unknown' if the architecture is unknown. .. py:class:: ChildTargetPlugin(target: dissect.target.Target) Bases: :py:obj:`Plugin` A Child target is a special plugin that can list more Targets. For example, :class:`~dissect.target.plugins.child.esxi.ESXiChildTargetPlugin` can list all of the Virtual Machines on the host. .. py:attribute:: __type__ .. py:method:: list_children() -> Iterator[dissect.target.helpers.record.ChildTargetRecord] :abstractmethod: Yield :class:`~dissect.target.helpers.record.ChildTargetRecord` records of all possible child targets on this target. .. py:function:: register(plugincls: Type[Plugin]) -> None Register a plugin, and put related data inside :attr:`PLUGINS`. This function uses the following private attributes that are set using decorators: - ``__exported__``: Set in :func:`export`. - ``__internal__``: Set in :func:`internal`. Additionally, ``register`` sets the following private attributes on the `plugincls`: - ``__plugin__``: Always set to ``True``. - ``__functions__``: A list of all the methods and properties that are ``__internal__`` or ``__exported__``. - ``__exports__``: A list of all the methods or properties that were explicitly exported. :param plugincls: A plugin class to register. :raises ValueError: If ``plugincls`` is not a subclass of :class:`Plugin`. .. py:function:: internal(*args, **kwargs) -> Callable Decorator to be used on plugin functions that should be internal only. Making a plugin internal means that it's only callable from the Python API and not through ``target-query``. This decorator adds the ``__internal__`` private attribute to a method or property. The attribute is always set to ``True``, to tell :func:`register` that it is an internal method or property. .. py:function:: arg(*args, **kwargs) -> Callable Decorator to be used on Plugin functions that accept additional command line arguments. Command line arguments can be added using the ``@arg`` decorator. Arguments to this decorator are directly forwarded to the ``ArgumentParser.add_argument`` function of ``argparse``. Resulting arguments are passed to the function using kwargs. The keyword argument name must match the argparse argument name. This decorator adds the ``__args__`` private attribute to a method or property. This attribute holds all the command line arguments that were added to the plugin function. .. py:function:: plugins(osfilter: Optional[type[OSPlugin]] = None, special_keys: set[str] = set(), only_special_keys: bool = False) -> Iterator[PluginDescriptor] Walk the ``PLUGINS`` tree and return plugins. If ``osfilter`` is specified, only plugins related to the provided OSPlugin, or plugins with no OS relation are returned. If ``osfilter`` is ``None``, all plugins will be returned. One exception to this is if the ``osfilter`` is a (sub-)class of DefaultPlugin, then plugins are returned as if no ``osfilter`` was specified. Another exeption to this are plugins in the ``PLUGINS`` tree which are under a key that starts with a '_'. Those are only returned if their exact key is specified in ``special_keys``. An exception to these exceptions is in the case of ``OSPlugin`` (sub-)class plugins and ``os_filter`` is not ``None``. These plugins live in the ``PLUGINS`` tree under the ``_os`` special key. Those plugins are only returned if they fully match the provided ``osfilter``. The ``only_special_keys`` option returns only the plugins which are under a special key that is defined in ``special_keys``. All filtering here will happen as stated in the above cases. :param osfilter: The optional OSPlugin to filter the returned plugins on. :param special_keys: Also return plugins which are under the special ('_') keys in this set. :param only_special_keys: Only return the plugins under the keys in ``special_keys`` and no others. :Yields: Plugins in the ``PLUGINS`` tree based on the given filter criteria. .. py:function:: os_plugins() -> Iterator[PluginDescriptor] Retrieve all OS plugin descriptors. .. py:function:: child_plugins() -> Iterator[PluginDescriptor] Retrieve all child plugin descriptors. .. py:function:: lookup(func_name: str, osfilter: Optional[type[OSPlugin]] = None) -> Iterator[PluginDescriptor] Lookup a plugin descriptor by function name. :param func_name: Function name to lookup. :param osfilter: The ``OSPlugin`` to use as template to find os specific plugins for. .. py:function:: get_plugins_by_func_name(func_name: str, osfilter: Optional[type[OSPlugin]] = None) -> Iterator[PluginDescriptor] Get a plugin descriptor by function name. :param func_name: Function name to lookup. :param osfilter: The ``OSPlugin`` to use as template to find os specific plugins for. .. py:function:: get_plugins_by_namespace(namespace: str, osfilter: Optional[type[OSPlugin]] = None) -> Iterator[PluginDescriptor] Get a plugin descriptor by namespace. :param namespace: Plugin namespace to match. :param osfilter: The ``OSPlugin`` to use as template to find os specific plugins for. .. py:function:: load(plugin_desc: PluginDescriptor) -> Type[Plugin] Helper function that loads a plugin from a given plugin description. :param plugin_desc: Plugin description as returned by plugin.lookup(). :returns: The plugin class. :raises PluginError: Raised when any other exception occurs while trying to load the plugin. .. py:function:: failed() -> list[dict[str, Any]] Return all plugins that failed to load. .. py:function:: save_plugin_import_failure(module: str) -> None Store errors that occurred during plugin import. .. py:function:: find_py_files(plugin_path: pathlib.Path) -> Iterator[pathlib.Path] Walk all the files and directories in ``plugin_path`` and return all files ending in ``.py``. Do not walk or yield paths containing the following names: - __pycache__ - __init__ Furthermore, it logs an error if ``plugin_path`` does not exist. :param plugin_path: The path to a directory or file to walk and filter. .. py:function:: load_module_from_name(module_path: str) -> None Load a module from ``module_path``. .. py:function:: generate() -> dict[str, Any] Internal function to generate the list of available plugins. Walks the plugins directory and imports any .py files in there. Plugins will be automatically registered due to the decorators on them. :returns: The global ``PLUGINS`` dictionary. .. py:function:: load_module_from_file(path: pathlib.Path, base_path: pathlib.Path) Loads a module from a file indicated by ``path`` relative to ``base_path``. The module is added to ``sys.modules`` so it can be found everywhere. :param path: The file to load as module. :param base_path: The base directory of the module. .. py:function:: load_modules_from_paths(plugin_dirs: list[pathlib.Path]) -> None Iterate over the ``plugin_dirs`` and load all ``.py`` files. .. py:function:: get_external_module_paths(path_list: list[pathlib.Path]) -> list[pathlib.Path] Create a deduplicated list of paths. .. py:function:: environment_variable_paths() -> list[pathlib.Path] .. py:class:: NamespacePlugin(target: dissect.target.Target) Bases: :py:obj:`Plugin` Base class for plugins. Plugins can optionally be namespaced by specifying the ``__namespace__`` class attribute. Namespacing results in your plugin needing to be prefixed with this namespace when being called. For example, if your plugin has specified ``test`` as namespace and a function called ``example``, you must call your plugin with ``test.example``:: A ``Plugin`` class has the following private class attributes: - ``__namespace__`` - ``__record_descriptors__`` With the following three being assigned in :func:`register`: - ``__plugin__`` - ``__functions__`` - ``__exports__`` Additionally, the methods and attributes of :class:`Plugin` receive more private attributes by using decorators. The :func:`export` decorator adds the following private attributes - ``__exported__`` - ``__output__``: Set with the :func:`export` decorator. - ``__record__``: Set with the :func:`export` decorator. The :func:`internal` decorator and :class:`InternalPlugin` set the ``__internal__`` attribute. Finally. :func:`args` decorator sets the ``__args__`` attribute. :param target: The :class:`~dissect.target.target.Target` object to load the plugin for. .. py:method:: check_compatible() -> None Perform a compatibility check with the target. This function should return ``None`` if the plugin is compatible with the current target (``self.target``). For example, check if a certain file exists. Otherwise it should raise an ``UnsupportedPluginError``. :raises UnsupportedPluginError: If the plugin could not be loaded. .. py:method:: __init_subclass_namespace__(**kwargs) .. py:method:: __init_subclass_subplugin__(**kwargs) .. py:method:: __init_subclass__(**kwargs) :classmethod: .. py:class:: InternalPlugin(target: dissect.target.Target) Bases: :py:obj:`Plugin` Parent class for internal plugins. InternalPlugin marks all non-private methods internal by default (same as ``@internal`` decorator). .. py:method:: __init_subclass__(**kwargs) :classmethod: .. py:class:: PluginFunction .. py:attribute:: name :type: str .. py:attribute:: path :type: str .. py:attribute:: output_type :type: str .. py:attribute:: class_object :type: type[Plugin] .. py:attribute:: method_name :type: str .. py:attribute:: plugin_desc :type: PluginDescriptor .. py:function:: plugin_function_index(target: Optional[dissect.target.Target]) -> tuple[dict[str, PluginDescriptor], set[str]] Returns an index-list for plugins. This list is used to match CLI expressions against to find the desired plugin. Also returns the roots to determine whether a CLI expression has to be compared to the plugin tree or parsed using legacy rules. .. py:function:: find_plugin_functions(target: Optional[dissect.target.Target], patterns: str, compatibility: bool = False, **kwargs) -> tuple[list[PluginFunction], set[str]] Finds plugins that match the target and the patterns. Given a target, a comma separated list of patterns and an optional compatibility flag, this function finds matching plugins, optionally checking compatibility and returns a list of plugin function descriptors (including output types).