dissect.target 3.17.dev11__py3-none-any.whl → 3.17.dev13__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.
@@ -63,6 +63,7 @@ class ServicesPlugin(Plugin):
63
63
  for key, value in configuration.items():
64
64
  _value = value or None
65
65
  _key = f"{segment}_{key}"
66
+ _key = _key.replace("-", "_")
66
67
  types.append(("string", _key))
67
68
  config.update({_key: _value})
68
69
  except FileNotFoundError:
@@ -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.dev11
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
@@ -215,7 +215,7 @@ dissect/target/plugins/os/unix/linux/modules.py,sha256=H1S5CkpXttCVwzE2Ylz3jkvrC
215
215
  dissect/target/plugins/os/unix/linux/netstat.py,sha256=MAC4ZdeNqcKpxT2ZMh1-7rjt4Pt_WQIRy7RChr7nlPk,1649
216
216
  dissect/target/plugins/os/unix/linux/proc.py,sha256=jm35fAasnNbObN2tpflwQuCfVYLDkTP2EDrzYG42ZSk,23354
217
217
  dissect/target/plugins/os/unix/linux/processes.py,sha256=sTQqZYPW-_gs7Z3f0wwsV6clUX4NK44GGyMiZToBIrg,1936
218
- dissect/target/plugins/os/unix/linux/services.py,sha256=1KZRITNJGJmWSnw320_bca_nlOj_TDqle-SQJAPEEWQ,4017
218
+ dissect/target/plugins/os/unix/linux/services.py,sha256=-d2y073mOXUM3XCzRgDVCRFR9eTLoVuN8FsZVewHzRg,4075
219
219
  dissect/target/plugins/os/unix/linux/sockets.py,sha256=l7Zq4J_xioyl8V7-Q9GLStOyYLNrcpKoWWWSzJaSIZQ,9765
220
220
  dissect/target/plugins/os/unix/linux/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
221
221
  dissect/target/plugins/os/unix/linux/android/_os.py,sha256=trmESlpHdwVu7wV18RevEhh_TsVyfKPFCd5Usb5-fSU,2056
@@ -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.dev11.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
340
- dissect.target-3.17.dev11.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
341
- dissect.target-3.17.dev11.dist-info/METADATA,sha256=IWvuvSd5YuV8CNcAMgSB_Wyovg2fnHzh7cZzU41v1TY,11300
342
- dissect.target-3.17.dev11.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
- dissect.target-3.17.dev11.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
344
- dissect.target-3.17.dev11.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
345
- dissect.target-3.17.dev11.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,,