dissect.target 3.17.dev12__py3-none-any.whl → 3.17.dev13__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,7 @@ import sys
7
7
  from collections import deque
8
8
  from dataclasses import dataclass
9
9
  from pathlib import Path
10
- from typing import Any, Generator, Iterable, List, Optional, Tuple
10
+ from typing import Any, Iterable, Iterator, Optional
11
11
 
12
12
  import structlog
13
13
  from flow.record import Record
@@ -25,10 +25,12 @@ from dissect.target.tools.dump.utils import (
25
25
  Compression,
26
26
  Serialization,
27
27
  cached_sink_writers,
28
- get_nested_attr,
29
28
  )
30
29
  from dissect.target.tools.utils import (
30
+ PluginFunction,
31
31
  configure_generic_arguments,
32
+ execute_function_on_target,
33
+ find_and_filter_plugins,
32
34
  process_generic_arguments,
33
35
  )
34
36
 
@@ -44,13 +46,13 @@ class RecordStreamElement:
44
46
  sink_path: Optional[Path] = None
45
47
 
46
48
 
47
- def get_targets(targets: List[str]) -> Generator[Target, None, None]:
49
+ def get_targets(targets: list[str]) -> Iterator[Target]:
48
50
  """Return a generator with `Target` objects for provided paths"""
49
51
  for target in Target.open_all(targets):
50
52
  yield target
51
53
 
52
54
 
53
- def execute_function(target: Target, function: str) -> Generator[TargetRecordDescriptor, None, None]:
55
+ def execute_function(target: Target, function: PluginFunction) -> TargetRecordDescriptor:
54
56
  """
55
57
  Execute function `function` on provided target `target` and return a generator
56
58
  with the records produced.
@@ -62,7 +64,7 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes
62
64
  local_log.debug("Function execution")
63
65
 
64
66
  try:
65
- target_attr = get_nested_attr(target, function)
67
+ output_type, target_attr, _ = execute_function_on_target(target, function)
66
68
  except UnsupportedPluginError:
67
69
  local_log.error("Function is not supported for target", exc_info=True)
68
70
  return
@@ -70,15 +72,8 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes
70
72
  local_log.error("Plugin error while executing function for target", exc_info=True)
71
73
  return
72
74
 
73
- # skip non-record outputs
74
- try:
75
- output = getattr(target_attr, "__output__", "default") if hasattr(target_attr, "__output__") else None
76
- except PluginError as e:
77
- local_log.error("Plugin error while fetching an attribute", exc_info=e)
78
- return
79
-
80
- if output != "record":
81
- local_log.warn("Output format is not supported", output=output)
75
+ if output_type != "record":
76
+ local_log.warn("Output format is not supported", output=output_type)
82
77
  return
83
78
 
84
79
  # no support for function-specific arguments
@@ -94,9 +89,9 @@ def execute_function(target: Target, function: str) -> Generator[TargetRecordDes
94
89
 
95
90
  def produce_target_func_pairs(
96
91
  targets: Iterable[Target],
97
- functions: List[str],
92
+ functions: str,
98
93
  state: DumpState,
99
- ) -> Generator[Tuple[Target, str], None, None]:
94
+ ) -> Iterator[tuple[Target, PluginFunction]]:
100
95
  """
101
96
  Return a generator with target and function pairs for execution.
102
97
 
@@ -107,20 +102,20 @@ def produce_target_func_pairs(
107
102
  pairs_to_skip.update((str(sink.target_path), sink.func) for sink in state.finished_sinks)
108
103
 
109
104
  for target in targets:
110
- for func in functions:
111
- if state and (target.path, func) in pairs_to_skip:
105
+ for func_def in find_and_filter_plugins(target, functions):
106
+ if state and (target.path, func_def.name) in pairs_to_skip:
112
107
  log.info(
113
108
  "Skipping target/func pair since its marked as done in provided state",
114
109
  target=target.path,
115
- func=func,
110
+ func=func_def.name,
116
111
  state=state.path,
117
112
  )
118
113
  continue
119
- yield (target, func)
120
- state.mark_as_finished(target, func)
114
+ yield (target, func_def)
115
+ state.mark_as_finished(target, func_def.name)
121
116
 
122
117
 
123
- def execute_functions(target_func_stream: Iterable[Tuple[Target, str]]) -> Generator[RecordStreamElement, None, None]:
118
+ def execute_functions(target_func_stream: Iterable[tuple[Target, str]]) -> Iterable[RecordStreamElement]:
124
119
  """
125
120
  Execute a function on a target for target / function pairs in the stream.
126
121
 
@@ -131,7 +126,7 @@ def execute_functions(target_func_stream: Iterable[Tuple[Target, str]]) -> Gener
131
126
  yield RecordStreamElement(target=target, func=func, record=record)
132
127
 
133
128
 
134
- def log_progress(stream: Iterable[Any], step_size: int = 1000) -> Generator[Any, None, None]:
129
+ def log_progress(stream: Iterable[Any], step_size: int = 1000) -> Iterable[Any]:
135
130
  """
136
131
  Log a number of items that went though the generator stream
137
132
  after every N element (N is configured in `step_size`).
@@ -155,7 +150,7 @@ def log_progress(stream: Iterable[Any], step_size: int = 1000) -> Generator[Any,
155
150
  def sink_records(
156
151
  record_stream: Iterable[RecordStreamElement],
157
152
  state: DumpState,
158
- ) -> Generator[RecordStreamElement, None, None]:
153
+ ) -> Iterator[RecordStreamElement]:
159
154
  """
160
155
  Persist records from the stream into appropriate sinks, per serialization, compression and record type.
161
156
  """
@@ -168,7 +163,7 @@ def sink_records(
168
163
  def persist_processing_state(
169
164
  record_stream: Iterable[RecordStreamElement],
170
165
  state: DumpState,
171
- ) -> Generator[RecordStreamElement, None, None]:
166
+ ) -> Iterator[RecordStreamElement]:
172
167
  """
173
168
  Keep track of the pipeline state in a persistent state object.
174
169
  """
@@ -179,8 +174,8 @@ def persist_processing_state(
179
174
 
180
175
 
181
176
  def execute_pipeline(
182
- targets: List[str],
183
- functions: List[str],
177
+ targets: list[str],
178
+ functions: str,
184
179
  output_dir: Path,
185
180
  serialization: Serialization,
186
181
  compression: Optional[Compression] = None,
@@ -297,7 +292,7 @@ def main():
297
292
  try:
298
293
  execute_pipeline(
299
294
  targets=args.targets,
300
- functions=args.function.split(","),
295
+ functions=args.function,
301
296
  output_dir=args.output,
302
297
  serialization=Serialization(args.serialization),
303
298
  compression=Compression(args.compression),
@@ -6,7 +6,7 @@ import json
6
6
  from contextlib import contextmanager
7
7
  from dataclasses import dataclass
8
8
  from pathlib import Path
9
- from typing import Any, Callable, Iterator, List, Optional, TextIO
9
+ from typing import Any, Callable, Iterator, Optional, TextIO
10
10
 
11
11
  import structlog
12
12
 
@@ -35,17 +35,20 @@ class Sink:
35
35
  record_count: int = 0
36
36
  size_bytes: int = 0
37
37
 
38
+ def __post_init__(self):
39
+ self.func = getattr(self.func, "name", self.func)
40
+
38
41
 
39
42
  @dataclass
40
43
  class DumpState:
41
- target_paths: List[str]
42
- functions: List[str]
44
+ target_paths: list[str]
45
+ functions: list[str]
43
46
  serialization: str
44
47
  compression: str
45
48
  start_time: datetime.datetime
46
49
  last_update_time: datetime.datetime
47
50
 
48
- sinks: List[Sink] = dataclasses.field(default_factory=list)
51
+ sinks: list[Sink] = dataclasses.field(default_factory=list)
49
52
 
50
53
  # Volatile properties
51
54
  output_dir: Optional[Path] = None
@@ -56,7 +59,7 @@ class DumpState:
56
59
  return sum(s.record_count for s in self.sinks)
57
60
 
58
61
  @property
59
- def finished_sinks(self) -> List[Sink]:
62
+ def finished_sinks(self) -> list[Sink]:
60
63
  return [sink for sink in self.sinks if not sink.is_dirty]
61
64
 
62
65
  @property
@@ -178,7 +181,7 @@ class DumpState:
178
181
  state.output_dir = output_dir
179
182
  return state
180
183
 
181
- def get_invalid_sinks(self) -> List[Sink]:
184
+ def get_invalid_sinks(self) -> list[Sink]:
182
185
  """Return sinks that have a mismatch between recorded size and a real file size"""
183
186
  invalid_sinks = []
184
187
  for sink in self.sinks:
@@ -214,8 +217,8 @@ class DumpState:
214
217
  def create_state(
215
218
  *,
216
219
  output_dir: Path,
217
- target_paths: List[str],
218
- functions: List[str],
220
+ target_paths: list[str],
221
+ functions: list[str],
219
222
  serialization: Serialization,
220
223
  compression: Compression = None,
221
224
  ) -> DumpState:
@@ -32,6 +32,7 @@ from flow.record.adapter.jsonfile import JsonfileWriter
32
32
  from flow.record.jsonpacker import JsonRecordPacker
33
33
 
34
34
  from dissect.target import Target
35
+ from dissect.target.plugin import PluginFunction
35
36
 
36
37
  log = structlog.get_logger(__name__)
37
38
 
@@ -69,14 +70,14 @@ def get_nested_attr(obj: Any, nested_attr: str) -> Any:
69
70
 
70
71
 
71
72
  @lru_cache(maxsize=DEST_DIR_CACHE_SIZE)
72
- def get_sink_dir_by_target(target: Target, function: str) -> Path:
73
- func_first_name, _, _ = function.partition(".")
73
+ def get_sink_dir_by_target(target: Target, function: PluginFunction) -> Path:
74
+ func_first_name, _, _ = function.name.partition(".")
74
75
  return Path(target.name) / func_first_name
75
76
 
76
77
 
77
78
  @functools.lru_cache(maxsize=DEST_DIR_CACHE_SIZE)
78
- def get_sink_dir_by_func(target: Target, function: str) -> Path:
79
- func_first_name, _, _ = function.partition(".")
79
+ def get_sink_dir_by_func(target: Target, function: PluginFunction) -> Path:
80
+ func_first_name, _, _ = function.name.partition(".")
80
81
  return Path(func_first_name) / target.name
81
82
 
82
83
 
@@ -26,6 +26,7 @@ from dissect.target.tools.utils import (
26
26
  catch_sigpipe,
27
27
  configure_generic_arguments,
28
28
  execute_function_on_target,
29
+ find_and_filter_plugins,
29
30
  generate_argparse_for_bound_method,
30
31
  generate_argparse_for_plugin_class,
31
32
  generate_argparse_for_unbound_method,
@@ -270,25 +271,13 @@ def main():
270
271
  basic_entries = []
271
272
  yield_entries = []
272
273
 
273
- # Keep a set of plugins that were already executed on the target.
274
- executed_plugins = set()
275
-
276
274
  first_seen_output_type = default_output_type
277
275
  cli_params_unparsed = rest
278
276
 
279
- func_defs, _ = find_plugin_functions(target, args.function, compatibility=False)
280
277
  excluded_funcs, _ = find_plugin_functions(target, args.excluded_functions, compatibility=False)
281
278
  excluded_func_paths = {excluded_func.path for excluded_func in excluded_funcs}
282
279
 
283
- for func_def in func_defs:
284
- if func_def.path in excluded_func_paths:
285
- continue
286
-
287
- # Avoid executing same plugin for multiple OSes (like hostname)
288
- if func_def.name in executed_plugins:
289
- continue
290
- executed_plugins.add(func_def.name)
291
-
280
+ for func_def in find_and_filter_plugins(target, args.function, excluded_func_paths):
292
281
  # If the default type is record (meaning we skip everything else)
293
282
  # and actual output type is not record, continue.
294
283
  # We perform this check here because plugins that require output files/dirs
@@ -21,6 +21,7 @@ from dissect.target.plugin import (
21
21
  OSPlugin,
22
22
  Plugin,
23
23
  PluginFunction,
24
+ find_plugin_functions,
24
25
  get_external_module_paths,
25
26
  load_modules_from_paths,
26
27
  )
@@ -289,3 +290,25 @@ def args_to_uri(targets: list[str], loader_name: str, rest: list[str]) -> list[s
289
290
  for target in targets:
290
291
  uris.append(f"{loader_name}://{target}" + (("?" + urllib.parse.urlencode(args)) if args else ""))
291
292
  return uris
293
+
294
+
295
+ def find_and_filter_plugins(
296
+ target: Target, functions: str, excluded_func_paths: set[str] = None
297
+ ) -> Iterator[PluginFunction]:
298
+ # Keep a set of plugins that were already executed on the target.
299
+ executed_plugins = set()
300
+ excluded_func_paths = excluded_func_paths or set()
301
+
302
+ func_defs, _ = find_plugin_functions(target, functions, compatibility=False)
303
+
304
+ for func_def in func_defs:
305
+ if func_def.path in excluded_func_paths:
306
+ continue
307
+
308
+ # Avoid executing same plugin for multiple OSes (like hostname)
309
+ if func_def.name in executed_plugins:
310
+ continue
311
+
312
+ executed_plugins.add(func_def.name)
313
+
314
+ yield func_def
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.17.dev12
3
+ Version: 3.17.dev13
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
@@ -320,14 +320,14 @@ dissect/target/tools/fs.py,sha256=cizCrW8rqdpT1irA8g6mslkaXX7CynWVQ7fvRUrcxNU,37
320
320
  dissect/target/tools/info.py,sha256=3smHr8I71yj3kCjsQ5nXkOHI9T_N8UwvkVa1CNOxB-s,5461
321
321
  dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
322
322
  dissect/target/tools/mount.py,sha256=L_0tSmiBdW4aSaF0vXjB0bAkTC0kmT2N1hrbW6s5Jow,3254
323
- dissect/target/tools/query.py,sha256=1LbvUKSmXOCMb4xqP3t86JkOgFzKlc7mLCqcczfLht8,16018
323
+ dissect/target/tools/query.py,sha256=6zz9SXS6YnHj7eguORS8Je7N4iM0i1PZDIQ-gyJ1nPY,15593
324
324
  dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
325
325
  dissect/target/tools/shell.py,sha256=6GF-mr4EpzX34G9sqTNKcccFJXhNTlrUqriRYIW7P7o,43544
326
- dissect/target/tools/utils.py,sha256=bhVZ3-8YynpHkBl4m1T4IpSpCArAXnEjjYwAFGW5JPg,10595
326
+ dissect/target/tools/utils.py,sha256=sQizexY3ui5vmWw4KOBLg5ecK3TPFjD-uxDqRn56ZTY,11304
327
327
  dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
328
- dissect/target/tools/dump/run.py,sha256=yHn9xl_VjasgiuLpjtZdnLW32QCbkwHfnnTPY6Ck_aw,9689
329
- dissect/target/tools/dump/state.py,sha256=ZBNz4ou2Xk20K1H8R83S1gq6qcqPvPPVAaPWzpKpX34,9123
330
- dissect/target/tools/dump/utils.py,sha256=nYcLQvPpDgzckM62hokGBh4z32DNH6d6oA8KelvoPMU,7564
328
+ dissect/target/tools/dump/run.py,sha256=aD84peRS4zHqC78fH7Vd4ni3m1ZmVP70LyMwBRvoDGY,9463
329
+ dissect/target/tools/dump/state.py,sha256=YYgCff0kZZ-tx27lJlc9LQ7AfoGnLK5Gyi796OnktA8,9205
330
+ dissect/target/tools/dump/utils.py,sha256=onQIWPPjkz8HmZMndVVP_4ywFixXwXGOjHi4x8D3Xqw,7645
331
331
  dissect/target/volumes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
332
332
  dissect/target/volumes/bde.py,sha256=3lCb7inPmu8MR5WxPWUauJ8t3Emymh9D-o2yCL-SB9g,3974
333
333
  dissect/target/volumes/ddf.py,sha256=X0yBIFbG7Z_CJDVqxEBLDynM_NoSwIWEKVsQFOS-Tn4,1749
@@ -336,10 +336,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
336
336
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
337
337
  dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
338
338
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
339
- dissect.target-3.17.dev12.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
340
- dissect.target-3.17.dev12.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
341
- dissect.target-3.17.dev12.dist-info/METADATA,sha256=1RcSWH_DwPFMSzZxN8iYmYFbcmtUKB1f-jrrz6w23GI,11300
342
- dissect.target-3.17.dev12.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
- dissect.target-3.17.dev12.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
344
- dissect.target-3.17.dev12.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
345
- dissect.target-3.17.dev12.dist-info/RECORD,,
339
+ dissect.target-3.17.dev13.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
340
+ dissect.target-3.17.dev13.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
341
+ dissect.target-3.17.dev13.dist-info/METADATA,sha256=r2xQoOApd3QGiaNTpuo27XM3t1YkKLaNSLkywaWejJ8,11300
342
+ dissect.target-3.17.dev13.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
+ dissect.target-3.17.dev13.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
344
+ dissect.target-3.17.dev13.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
345
+ dissect.target-3.17.dev13.dist-info/RECORD,,