dissect.target 3.9.dev36__py3-none-any.whl → 3.9.dev38__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dissect/target/plugin.py CHANGED
@@ -13,7 +13,7 @@ import logging
13
13
  import os
14
14
  import sys
15
15
  import traceback
16
- from dataclasses import dataclass
16
+ from dataclasses import dataclass, field
17
17
  from pathlib import Path
18
18
  from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Type
19
19
 
@@ -811,13 +811,13 @@ class InternalPlugin(Plugin):
811
811
  return cls
812
812
 
813
813
 
814
- @dataclass
814
+ @dataclass(frozen=True, eq=True)
815
815
  class PluginFunction:
816
816
  name: str
817
817
  output_type: str
818
818
  class_object: str
819
819
  method_name: str
820
- plugin_desc: dict
820
+ plugin_desc: dict = field(hash=False)
821
821
 
822
822
 
823
823
  def plugin_function_index(target: Target) -> tuple[dict[str, Any], set[str]]:
@@ -831,7 +831,9 @@ def plugin_function_index(target: Target) -> tuple[dict[str, Any], set[str]]:
831
831
  rootset = set()
832
832
 
833
833
  def all_plugins():
834
- yield from plugins()
834
+ # Filter out plugins based on the target os
835
+ os_type = type(target._os) if target._os else None
836
+ yield from plugins(os_type)
835
837
  yield from os_plugins()
836
838
  yield from child_plugins() # Doesn't export anything but added for completeness.
837
839
 
@@ -885,33 +887,34 @@ def find_plugin_functions(target: Target, patterns: str, compatibility: bool = F
885
887
  pattern += "*"
886
888
 
887
889
  if wildcard or treematch:
888
- for index_name, func in functions.items():
889
- if fnmatch.fnmatch(index_name, pattern):
890
- method_name = index_name.split(".")[-1]
891
- loaded_plugin_object = load(func)
890
+ for index_name in fnmatch.filter(functions.keys(), pattern):
891
+ func = functions[index_name]
892
892
 
893
- # Skip plugins that don't want to be found by wildcards
894
- if not loaded_plugin_object.__findable__:
895
- continue
893
+ method_name = index_name.split(".")[-1]
894
+ loaded_plugin_object = load(func)
895
+
896
+ # Skip plugins that don't want to be found by wildcards
897
+ if not loaded_plugin_object.__findable__:
898
+ continue
896
899
 
897
- fobject = inspect.getattr_static(loaded_plugin_object, method_name)
900
+ fobject = inspect.getattr_static(loaded_plugin_object, method_name)
898
901
 
899
- if compatibility:
900
- try:
901
- if not loaded_plugin_object(target).is_compatible():
902
- continue
903
- except Exception:
902
+ if compatibility:
903
+ try:
904
+ if not loaded_plugin_object(target).is_compatible():
904
905
  continue
906
+ except Exception:
907
+ continue
905
908
 
906
- result.append(
907
- PluginFunction(
908
- name=index_name,
909
- class_object=loaded_plugin_object,
910
- method_name=method_name,
911
- output_type=getattr(fobject, "__output__", "text"),
912
- plugin_desc=func,
913
- )
909
+ result.append(
910
+ PluginFunction(
911
+ name=index_name,
912
+ class_object=loaded_plugin_object,
913
+ method_name=method_name,
914
+ output_type=getattr(fobject, "__output__", "text"),
915
+ plugin_desc=func,
914
916
  )
917
+ )
915
918
  else:
916
919
  # otherwise match using ~ classic style
917
920
  if pattern.find(".") > -1:
@@ -920,15 +923,15 @@ def find_plugin_functions(target: Target, patterns: str, compatibility: bool = F
920
923
  funcname = pattern
921
924
  namespace = None
922
925
 
923
- plugindesc = None
924
- for index_name, func in functions.items():
926
+ plugin_descriptions = []
927
+ for _, func in functions.items():
925
928
  nsmatch = namespace and func["namespace"] == namespace and funcname in func["exports"]
926
929
  fmatch = not namespace and not func["namespace"] and funcname in func["exports"]
927
930
  if nsmatch or fmatch:
928
- plugindesc = func
931
+ plugin_descriptions.append(func)
929
932
 
930
- if plugindesc:
931
- loaded_plugin_object = load(plugindesc)
933
+ for description in plugin_descriptions:
934
+ loaded_plugin_object = load(description)
932
935
  fobject = inspect.getattr_static(loaded_plugin_object, funcname)
933
936
 
934
937
  if compatibility and not loaded_plugin_object(target).is_compatible():
@@ -936,11 +939,12 @@ def find_plugin_functions(target: Target, patterns: str, compatibility: bool = F
936
939
 
937
940
  result.append(
938
941
  PluginFunction(
939
- name=f"{plugindesc['module']}.{pattern}",
942
+ name=f"{description['module']}.{pattern}",
940
943
  class_object=loaded_plugin_object,
941
944
  method_name=funcname,
942
945
  output_type=getattr(fobject, "__output__", "text"),
943
- plugin_desc=plugindesc,
946
+ plugin_desc=description,
944
947
  )
945
948
  )
946
- return result
949
+
950
+ return list(set(result))
@@ -202,10 +202,16 @@ def main():
202
202
  basic_entries = []
203
203
  yield_entries = []
204
204
 
205
+ # Keep a set of plugins that were already executed on the target.
206
+ executed_plugins = set()
207
+
205
208
  first_seen_output_type = default_output_type
206
209
  cli_params_unparsed = rest
207
210
 
208
- for func_def in funcs:
211
+ for func_def in find_plugin_functions(target, args.function, False):
212
+ if func_def.method_name in executed_plugins:
213
+ continue
214
+
209
215
  try:
210
216
  output_type, result, cli_params_unparsed = execute_function_on_target(
211
217
  target, func_def, cli_params_unparsed
@@ -213,17 +219,17 @@ def main():
213
219
  except UnsupportedPluginError as e:
214
220
  target.log.error(
215
221
  "Unsupported plugin for `%s`: %s",
216
- func,
222
+ func_def,
217
223
  e.root_cause_str(),
218
224
  )
219
225
 
220
226
  target.log.debug("", exc_info=e)
221
227
  continue
222
228
  except PluginNotFoundError:
223
- target.log.error("Cannot find plugin `%s`", func)
229
+ target.log.error("Cannot find plugin `%s`", func_def)
224
230
  continue
225
231
  except Exception:
226
- target.log.error("Exception while executing function `%s`", func, exc_info=True)
232
+ target.log.error("Exception while executing function `%s`", func_def, exc_info=True)
227
233
  continue
228
234
 
229
235
  if first_seen_output_type and output_type != first_seen_output_type:
@@ -236,7 +242,7 @@ def main():
236
242
  "does not match first seen output `%s`."
237
243
  ),
238
244
  output_type,
239
- func,
245
+ func_def,
240
246
  first_seen_output_type,
241
247
  )
242
248
  parser.exit()
@@ -244,12 +250,14 @@ def main():
244
250
  if not first_seen_output_type:
245
251
  first_seen_output_type = output_type
246
252
 
253
+ executed_plugins.add(func_def.method_name)
254
+
247
255
  if output_type == "record":
248
256
  record_entries.append(result)
249
257
  elif output_type == "yield":
250
258
  yield_entries.append(result)
251
259
  elif output_type == "none":
252
- target.log.info("No result for function `%s` (output type is set to 'none')", func)
260
+ target.log.info("No result for function `%s` (output type is set to 'none')", func_def)
253
261
  continue
254
262
  else:
255
263
  basic_entries.append(result)
@@ -3,7 +3,7 @@ import inspect
3
3
  import json
4
4
  from datetime import datetime
5
5
  from pathlib import Path
6
- from typing import Any, Callable, Dict, List, Optional, Tuple, Type
6
+ from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Type, Union
7
7
 
8
8
  from dissect.target import Target
9
9
  from dissect.target.exceptions import UnsupportedPluginError
@@ -129,13 +129,16 @@ def generate_argparse_for_plugin(
129
129
  return generate_argparse_for_plugin_class(plugin_instance.__class__, usage_tmpl=usage_tmpl)
130
130
 
131
131
 
132
- def plugin_factory(target: Target, plugin_class: type, funcname: str) -> tuple[Plugin, str]:
132
+ def plugin_factory(target: Target, plugin: Union[type, object], funcname: str) -> tuple[Plugin, str]:
133
133
  if TargetdLoader.instance:
134
134
  return target.get_function(funcname)
135
+
136
+ if isinstance(plugin, type):
137
+ plugin_obj = plugin(target)
138
+ target_attr = getattr(plugin_obj, funcname)
139
+ return plugin_obj, target_attr
135
140
  else:
136
- plugin_obj = plugin_class(target)
137
- target_attr = getattr(plugin_obj, funcname)
138
- return plugin_obj, target_attr
141
+ return plugin, getattr(plugin, funcname)
139
142
 
140
143
 
141
144
  def execute_function_on_target(
@@ -149,9 +152,40 @@ def execute_function_on_target(
149
152
 
150
153
  cli_params = cli_params or []
151
154
 
152
- plugin_class = func.class_object
155
+ target_attr = get_target_attribute(target, func)
156
+ plugin_method, parser = plugin_function_with_argparser(target_attr)
157
+
158
+ if parser:
159
+ parsed_params, cli_params = parser.parse_known_args(cli_params)
160
+ method_kwargs = vars(parsed_params)
161
+ value = plugin_method(**method_kwargs)
162
+ else:
163
+ value = target_attr
164
+
165
+ output_type = getattr(plugin_method, "__output__", "default") if plugin_method else "default"
166
+ return (output_type, value, cli_params)
167
+
168
+
169
+ def get_target_attribute(target: Target, func: PluginFunction) -> Union[Plugin, Callable]:
170
+ """Retrieves the function attribute from the target.
171
+
172
+ If the function does not exist yet, it will attempt to load it into the target.
173
+
174
+ Args:
175
+ target: The target we wish to run the function on.
176
+ func: The function to run on the target.
153
177
 
154
- if issubclass(plugin_class, OSPlugin):
178
+ Returns:
179
+ The function, either plugin or a callable to execute.
180
+
181
+ Raises:
182
+ UnsupportedPluginError: When the function was incompatible with the target.
183
+ """
184
+ plugin_class = func.class_object
185
+ if target.has_function(func.method_name):
186
+ # If the function is already attached, use the one inside the target.
187
+ plugin_class, _ = target.get_function(func.method_name)
188
+ elif issubclass(plugin_class, OSPlugin):
155
189
  # OS plugin does not need to be added
156
190
  plugin_class = target._os_plugin
157
191
  else:
@@ -162,7 +196,14 @@ def execute_function_on_target(
162
196
  f"Unsupported function `{func.method_name}` for target with plugin {func.class_object}", cause=e
163
197
  )
164
198
 
165
- plugin_obj, target_attr = plugin_factory(target, plugin_class, func.method_name)
199
+ _, target_attr = plugin_factory(target, plugin_class, func.method_name)
200
+ return target_attr
201
+
202
+
203
+ def plugin_function_with_argparser(
204
+ target_attr: Union[Plugin, Callable]
205
+ ) -> tuple[Optional[Iterator], Optional[argparse.ArgumentParser]]:
206
+ """Resolves which plugin function to execute, and creates the argument parser for said plugin."""
166
207
  plugin_method = None
167
208
  parser = None
168
209
 
@@ -178,16 +219,7 @@ def execute_function_on_target(
178
219
  elif callable(target_attr):
179
220
  plugin_method = target_attr
180
221
  parser = generate_argparse_for_bound_method(target_attr)
181
-
182
- if parser:
183
- parsed_params, cli_params = parser.parse_known_args(cli_params)
184
- method_kwargs = vars(parsed_params)
185
- value = plugin_method(**method_kwargs)
186
- else:
187
- value = target_attr
188
-
189
- output_type = getattr(plugin_method, "__output__", "default") if plugin_method else "default"
190
- return (output_type, value, cli_params)
222
+ return plugin_method, parser
191
223
 
192
224
 
193
225
  def persist_execution_report(output_dir: Path, report_data: Dict, timestamp: datetime) -> Path:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.9.dev36
3
+ Version: 3.9.dev38
4
4
  Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -21,7 +21,7 @@ Requires-Dist: dissect.ntfs (<4.0.dev,>=3.4.dev)
21
21
  Requires-Dist: dissect.regf (<4.0.dev,>=3.3.dev)
22
22
  Requires-Dist: dissect.util (<4.0.dev,>=3.0.dev)
23
23
  Requires-Dist: dissect.volume (<4.0.dev,>=3.0.dev)
24
- Requires-Dist: flow.record (~=3.9)
24
+ Requires-Dist: flow.record (~=3.10)
25
25
  Requires-Dist: structlog
26
26
  Provides-Extra: full
27
27
  Requires-Dist: asn1crypto ; extra == 'full'
@@ -3,7 +3,7 @@ dissect/target/container.py,sha256=R8M9EE7DqKq8DeMuekcpR1nxtZ827zuqmTmO4s7PYkg,7
3
3
  dissect/target/exceptions.py,sha256=w3qNa4dtGpxUkOCfmAQtvseQrpQq9qmO9wIbztRlIDQ,2273
4
4
  dissect/target/filesystem.py,sha256=YJfuRzd37DIm91cVPlMZK-XjiJ1UCZG93htaYcSUrIk,46276
5
5
  dissect/target/loader.py,sha256=_gLb3nTAlyS0MS3JEFFD44Qk7X5MQMfhoRTFWaH7sA4,7072
6
- dissect/target/plugin.py,sha256=qF0okKg7TxYfiLVxpCWhb18gvDsNtr9nCYD7kSeaYIU,31955
6
+ dissect/target/plugin.py,sha256=GLoCrD1eKMy_tbRkwd8YzkSdPNdym5M0-e-aR8kTt10,32098
7
7
  dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
8
8
  dissect/target/target.py,sha256=TgDY-yAsReOQOG-Phz_m1vdNucdbk9fUI_RMZpMeYG8,28334
9
9
  dissect/target/volume.py,sha256=vHBXdDttpiu-Q_oWycNM7fdJ5N8Ob7-i_UBJK9DEs24,15027
@@ -248,10 +248,10 @@ dissect/target/tools/fs.py,sha256=dz6IEz1CKTVsSuMu6oAqdkc1qfIfqnMGABHSDDKKSDM,36
248
248
  dissect/target/tools/info.py,sha256=ZYRKLANt99uhpMcP5eHj6WegWAffqq8tS8MNj5txwqE,5141
249
249
  dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
250
250
  dissect/target/tools/mount.py,sha256=gPez-PLaeof7a6s7spJ23F_JcdQ7cUOPx5FX42rqF4A,2500
251
- dissect/target/tools/query.py,sha256=Btt_PMVAWIYHhPLg0b8u5e_TDdG8CG5xapxLu82dbSE,11466
251
+ dissect/target/tools/query.py,sha256=PHj-lNgcHSOQ401Hlc-HvpupAx4wnQNPeFOrxYyDJg0,11779
252
252
  dissect/target/tools/reg.py,sha256=37g_Xdb5ZbYAkMgQFmZNdKM_wWP9Bcw2Kk6quo1gwZ4,2147
253
253
  dissect/target/tools/shell.py,sha256=HICeIN5kCZYyGmAm_riWO9xrGnQmOzSp-Oici4QeO6Y,36003
254
- dissect/target/tools/utils.py,sha256=Et3Z67JKv-zzKLhJ6VWn6oiZ9GLoSt6em3sUxNJjyoc,7191
254
+ dissect/target/tools/utils.py,sha256=yDdQxAlWpwlOSnZlN9-ktreMxG_q-NXY_1Z8f7lnAQI,8446
255
255
  dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
256
  dissect/target/tools/dump/run.py,sha256=yHn9xl_VjasgiuLpjtZdnLW32QCbkwHfnnTPY6Ck_aw,9689
257
257
  dissect/target/tools/dump/state.py,sha256=ZBNz4ou2Xk20K1H8R83S1gq6qcqPvPPVAaPWzpKpX34,9123
@@ -261,10 +261,10 @@ dissect/target/volumes/bde.py,sha256=gYGg5yF9MNARwNzEkrEfZmKkxyZW4rhLkpdnPJCbhGk
261
261
  dissect/target/volumes/disk.py,sha256=95grSsPt1BLVpKwTclwQYzPFGKTkFFqapIk0RoGWf38,968
262
262
  dissect/target/volumes/lvm.py,sha256=_kIB1mdRs1OFhRgoT4VEP5Fv8imQnI7oQ_ie4x710tQ,1814
263
263
  dissect/target/volumes/vmfs.py,sha256=mlAJ8278tYaoRjk1u6tFFlCaDQUrVu5ZZE4ikiFvxi8,1707
264
- dissect.target-3.9.dev36.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
265
- dissect.target-3.9.dev36.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
266
- dissect.target-3.9.dev36.dist-info/METADATA,sha256=gvoKMDbMeKEx-jaq_u5oxUS4PW4FrDShGItVS8LVTKA,10233
267
- dissect.target-3.9.dev36.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
268
- dissect.target-3.9.dev36.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
269
- dissect.target-3.9.dev36.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
270
- dissect.target-3.9.dev36.dist-info/RECORD,,
264
+ dissect.target-3.9.dev38.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
265
+ dissect.target-3.9.dev38.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
266
+ dissect.target-3.9.dev38.dist-info/METADATA,sha256=F2OhtulG3gqAU4z12JPG5uc8kjBJmrZJ8Jcgl86OdjI,10234
267
+ dissect.target-3.9.dev38.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
268
+ dissect.target-3.9.dev38.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
269
+ dissect.target-3.9.dev38.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
270
+ dissect.target-3.9.dev38.dist-info/RECORD,,