dissect.target 3.20.dev41__py3-none-any.whl → 3.20.dev43__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,8 @@
1
+ import json
2
+
1
3
  from dissect.target.helpers.docs import INDENT_STEP, get_docstring
2
4
  from dissect.target.loader import LOADERS_BY_SCHEME
3
- from dissect.target.plugin import Plugin, export
5
+ from dissect.target.plugin import Plugin, arg, export
4
6
 
5
7
 
6
8
  class LoaderListPlugin(Plugin):
@@ -10,7 +12,12 @@ class LoaderListPlugin(Plugin):
10
12
  pass
11
13
 
12
14
  @export(output="none")
13
- def loaders(self) -> None:
15
+ # NOTE: We would prefer to re-use arguments across plugins from argparse in query.py, but that is not possible yet.
16
+ # For now we use --as-json, but in the future this should be changed to inherit --json from target-query.
17
+ # https://github.com/fox-it/dissect.target/pull/841
18
+ # https://github.com/fox-it/dissect.target/issues/889
19
+ @arg("--as-json", dest="as_json", action="store_true")
20
+ def loaders(self, as_json: bool = False) -> None:
14
21
  """List the available loaders."""
15
22
 
16
23
  loaders_info = {}
@@ -21,6 +28,12 @@ class LoaderListPlugin(Plugin):
21
28
  except ImportError:
22
29
  continue
23
30
 
24
- print("Available loaders:")
25
- for loader_name, loader_description in sorted(loaders_info.items()):
26
- print(f"{INDENT_STEP}{loader_name} - {loader_description}")
31
+ loaders = sorted(loaders_info.items())
32
+
33
+ if as_json:
34
+ print(json.dumps([{"name": name, "description": desc} for name, desc in loaders]), end="")
35
+
36
+ else:
37
+ print("Available loaders:")
38
+ for loader_name, loader_description in loaders:
39
+ print(f"{INDENT_STEP}{loader_name} - {loader_description}")
@@ -1,5 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+ import json
1
4
  import textwrap
2
- from typing import Dict, List, Type, Union
5
+ from typing import Iterator, Type
3
6
 
4
7
  from dissect.target import plugin
5
8
  from dissect.target.helpers.docs import INDENT_STEP, get_plugin_overview
@@ -23,7 +26,8 @@ def categorize_plugins(plugins_selection: list[dict] = None) -> dict:
23
26
  return output_dict
24
27
 
25
28
 
26
- def get_exported_plugins():
29
+ def get_exported_plugins() -> list:
30
+ """Returns list of exported plugins."""
27
31
  return [p for p in plugin.plugins() if len(p["exports"])]
28
32
 
29
33
 
@@ -50,10 +54,10 @@ def update_dict_recursive(source_dict: dict, updated_dict: dict) -> dict:
50
54
 
51
55
 
52
56
  def output_plugin_description_recursive(
53
- structure_dict: Union[Dict, Plugin],
57
+ structure_dict: dict | Plugin,
54
58
  print_docs: bool,
55
- indentation_step=0,
56
- ) -> List[str]:
59
+ indentation_step: int = 0,
60
+ ) -> list[str]:
57
61
  """Create plugin overview with identations."""
58
62
 
59
63
  if isinstance(structure_dict, type) and issubclass(structure_dict, Plugin):
@@ -78,10 +82,10 @@ def get_plugin_description(
78
82
 
79
83
 
80
84
  def get_description_dict(
81
- structure_dict: Dict,
85
+ structure_dict: dict,
82
86
  print_docs: bool,
83
87
  indentation_step: int,
84
- ) -> List[str]:
88
+ ) -> list[str]:
85
89
  """Returns a list of indented descriptions."""
86
90
 
87
91
  output_descriptions = []
@@ -105,10 +109,17 @@ class PluginListPlugin(Plugin):
105
109
 
106
110
  @export(output="none", cache=False)
107
111
  @arg("--docs", dest="print_docs", action="store_true")
108
- def plugins(self, plugins: list[dict] = None, print_docs: bool = False) -> None:
109
- """Print all registered plugins to stdout."""
112
+ # NOTE: We would prefer to re-use arguments across plugins from argparse in query.py, but that is not possible yet.
113
+ # For now we use --as-json, but in the future this should be changed to inherit --json from target-query.
114
+ # https://github.com/fox-it/dissect.target/pull/841
115
+ # https://github.com/fox-it/dissect.target/issues/889
116
+ @arg("--as-json", dest="as_json", action="store_true")
117
+ def plugins(self, plugins: list[Plugin] = None, print_docs: bool = False, as_json: bool = False) -> None:
118
+ """Print all available plugins."""
119
+
120
+ dict_plugins = list({p.path: p.plugin_desc for p in plugins}.values())
121
+ categorized_plugins = dict(sorted(categorize_plugins(dict_plugins).items()))
110
122
 
111
- categorized_plugins = dict(sorted(categorize_plugins(plugins).items()))
112
123
  plugin_descriptions = output_plugin_description_recursive(categorized_plugins, print_docs)
113
124
 
114
125
  plugins_list = textwrap.indent(
@@ -142,4 +153,32 @@ class PluginListPlugin(Plugin):
142
153
  "Failed to load:",
143
154
  failed_list,
144
155
  ]
145
- print("\n".join(output_lines))
156
+
157
+ if as_json:
158
+ out = {"loaded": list(generate_plugins_json(plugins))}
159
+
160
+ if failed_plugins := plugin.failed():
161
+ out["failed"] = [
162
+ {"module": p["module"], "stacktrace": "".join(p["stacktrace"])} for p in failed_plugins
163
+ ]
164
+
165
+ print(json.dumps(out), end="")
166
+
167
+ else:
168
+ print("\n".join(output_lines))
169
+
170
+
171
+ def generate_plugins_json(plugins: list[Plugin]) -> Iterator[dict]:
172
+ """Generates JSON output of a list of :class:`Plugin`s."""
173
+
174
+ for p in plugins:
175
+ func = getattr(p.class_object, p.method_name)
176
+ description = getattr(func, "__doc__", None)
177
+ summary = description.split("\n\n", 1)[0].strip() if description else None
178
+
179
+ yield {
180
+ "name": p.name,
181
+ "output": p.output_type,
182
+ "description": summary,
183
+ "path": p.path,
184
+ }
@@ -169,31 +169,40 @@ def main():
169
169
  # Show the list of available plugins for the given optional target and optional
170
170
  # search pattern, only display plugins that can be applied to ANY targets
171
171
  if args.list:
172
- collected_plugins = {}
172
+ collected_plugins = []
173
173
 
174
174
  if targets:
175
175
  for plugin_target in Target.open_all(targets, args.children):
176
176
  funcs, _ = find_plugin_functions(plugin_target, args.list, compatibility=True, show_hidden=True)
177
177
  for func in funcs:
178
- collected_plugins[func.path] = func.plugin_desc
178
+ collected_plugins.append(func)
179
179
  else:
180
180
  funcs, _ = find_plugin_functions(Target(), args.list, compatibility=False, show_hidden=True)
181
181
  for func in funcs:
182
- collected_plugins[func.path] = func.plugin_desc
182
+ collected_plugins.append(func)
183
183
 
184
- # Display in a user friendly manner
185
184
  target = Target()
186
185
  fparser = generate_argparse_for_bound_method(target.plugins, usage_tmpl=USAGE_FORMAT_TMPL)
187
186
  fargs, rest = fparser.parse_known_args(rest)
188
187
 
188
+ # Display in a user friendly manner
189
189
  if collected_plugins:
190
- target.plugins(list(collected_plugins.values()))
190
+ if args.json:
191
+ print('{"plugins": ', end="")
192
+ target.plugins(collected_plugins, as_json=args.json)
191
193
 
192
194
  # No real targets specified, show the available loaders
193
195
  if not targets:
194
196
  fparser = generate_argparse_for_bound_method(target.loaders, usage_tmpl=USAGE_FORMAT_TMPL)
195
197
  fargs, rest = fparser.parse_known_args(rest)
196
- target.loaders(**vars(fargs))
198
+ del fargs.as_json
199
+ if args.json:
200
+ print(', "loaders": ', end="")
201
+ target.loaders(**vars(fargs), as_json=args.json)
202
+
203
+ if args.json:
204
+ print("}")
205
+
197
206
  parser.exit()
198
207
 
199
208
  if not targets:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.20.dev41
3
+ Version: 3.20.dev43
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
@@ -186,10 +186,10 @@ dissect/target/plugins/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
186
186
  dissect/target/plugins/general/config.py,sha256=Mdy9uhWn4OJ96zfXpLgjVifV5SrViqHnpSnKhC1mjZE,3432
187
187
  dissect/target/plugins/general/default.py,sha256=8W_9JV3jKEeETlyTrB25sACoIIFmmO8wlVU5Zoi51W0,1425
188
188
  dissect/target/plugins/general/example.py,sha256=mYAbhtfQmUBj2L2C1DFt9bWpI7rQLJwCIYUsNLcA_pc,6053
189
- dissect/target/plugins/general/loaders.py,sha256=B-9GA4ftiFIpxbc8BjliTNpQREN3lvNlUhJf2N3fhjM,887
189
+ dissect/target/plugins/general/loaders.py,sha256=z_t55Q1XNjmTOxq0E4tCwpZ-utFyxiLKyAJIFgJMlJs,1508
190
190
  dissect/target/plugins/general/network.py,sha256=J8aMfUJ7dgwqpaXzZpHHyOUYg-cPef2Qaa3krUj-A-Q,3225
191
191
  dissect/target/plugins/general/osinfo.py,sha256=oU-vmMiA-oaSEQWTSyn6-yQiH2sLQT6aTQHRd0677wo,1415
192
- dissect/target/plugins/general/plugins.py,sha256=pn6uwfzbLnVTBpel5_GRPOFU3XEAMzbsiFY1UWKkWjc,4575
192
+ dissect/target/plugins/general/plugins.py,sha256=9KJ70YvYwBfxt19C9yISv8YE4mOdHNvP16fTCTHC68U,6033
193
193
  dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6vWVtER9vjTg,6651
194
194
  dissect/target/plugins/general/users.py,sha256=yy9gvRXfN9BT71v4Xqo5hpwfgN9he9Otu8TBPZ_Tegs,3009
195
195
  dissect/target/plugins/os/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -356,7 +356,7 @@ dissect/target/tools/fsutils.py,sha256=q0t9gFwKHaPr2Ya-MN2o4LsYledde7kp2DZZTd8ro
356
356
  dissect/target/tools/info.py,sha256=8nnbqFUYeo4NLPE7ORcTBcDL-TioGB2Nqc1TKcu5qdY,5715
357
357
  dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
358
358
  dissect/target/tools/mount.py,sha256=8GRYnu4xEmFBHxuIZAYhOMyyTGX8fat1Ou07DNiUnW4,3945
359
- dissect/target/tools/query.py,sha256=e-yAN9zdQjuOiTuoOQoo17mVEQGGcOgaA9YkF4GYpkM,15394
359
+ dissect/target/tools/query.py,sha256=OYWVmCx2nFx85x1r8Y6D17UdUIi8PJm304xBfT-H8vs,15605
360
360
  dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
361
361
  dissect/target/tools/shell.py,sha256=qY-JIwFQKBHTbqOiFxeO9OYeOlesQlx0r8PHghSAV8I,54207
362
362
  dissect/target/tools/utils.py,sha256=JJZDSso1CEK2sv4Z3HJNgqxH6G9S5lbmV-C3h-XmcMo,12035
@@ -373,10 +373,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
373
373
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
374
374
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
375
375
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
376
- dissect.target-3.20.dev41.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
377
- dissect.target-3.20.dev41.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
378
- dissect.target-3.20.dev41.dist-info/METADATA,sha256=L31RLE9aRvflCgG4WQ89JHQgFlD-1TbAlxgnXJFtY4U,12897
379
- dissect.target-3.20.dev41.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
380
- dissect.target-3.20.dev41.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
381
- dissect.target-3.20.dev41.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
382
- dissect.target-3.20.dev41.dist-info/RECORD,,
376
+ dissect.target-3.20.dev43.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
377
+ dissect.target-3.20.dev43.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
378
+ dissect.target-3.20.dev43.dist-info/METADATA,sha256=FO2hxtOkklCzr7ZrCmD1SeMtL5Dqy8jufOSQFRUQVRY,12897
379
+ dissect.target-3.20.dev43.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
380
+ dissect.target-3.20.dev43.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
381
+ dissect.target-3.20.dev43.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
382
+ dissect.target-3.20.dev43.dist-info/RECORD,,