dissect.target 3.20.dev64__py3-none-any.whl → 3.20.1__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.
@@ -0,0 +1,338 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass, field
5
+ from datetime import datetime, timezone
6
+ from ipaddress import ip_address, ip_interface
7
+ from typing import TYPE_CHECKING, Any, Iterator, Literal, NamedTuple
8
+
9
+ from dissect.target.helpers import configutil
10
+ from dissect.target.helpers.record import UnixInterfaceRecord
11
+ from dissect.target.helpers.utils import to_list
12
+ from dissect.target.plugins.general.network import NetworkPlugin
13
+
14
+ if TYPE_CHECKING:
15
+ from ipaddress import IPv4Address, IPv4Interface, IPv6Address, IPv6Interface
16
+
17
+ from dissect.target import Target
18
+ from dissect.target.target import TargetPath
19
+
20
+ NetAddress = IPv4Address | IPv6Address
21
+ NetInterface = IPv4Interface | IPv6Interface
22
+
23
+
24
+ class LinuxNetworkPlugin(NetworkPlugin):
25
+ """Linux network interface plugin."""
26
+
27
+ def _interfaces(self) -> Iterator[UnixInterfaceRecord]:
28
+ """Try all available network configuration managers and aggregate the results."""
29
+ for manager_cls in MANAGERS:
30
+ manager: LinuxNetworkConfigParser = manager_cls(self.target)
31
+ yield from manager.interfaces()
32
+
33
+
34
+ VlanIdByInterface = dict[str, set[int]]
35
+
36
+
37
+ class LinuxNetworkConfigParser:
38
+ def __init__(self, target: Target):
39
+ self._target = target
40
+
41
+ def _config_files(self, config_paths: list[str], glob: str) -> list[TargetPath]:
42
+ """Returns all configuration files in config_paths matching the given extension."""
43
+ all_files = []
44
+ for config_path in config_paths:
45
+ paths = self._target.fs.path(config_path).glob(glob)
46
+ all_files.extend(config_file for config_file in paths if config_file.is_file())
47
+
48
+ return sorted(all_files, key=lambda p: p.stem)
49
+
50
+ def interfaces(self) -> Iterator[UnixInterfaceRecord]:
51
+ """Parse network interfaces from configuration files."""
52
+ yield from ()
53
+
54
+
55
+ class NetworkManagerConfigParser(LinuxNetworkConfigParser):
56
+ """NetworkManager configuration parser.
57
+
58
+ NetworkManager configuration files are generally in an INI-like format.
59
+ Note that Red Hat and Fedora deprecated ifcfg files.
60
+ Documentation: https://networkmanager.dev/docs/api/latest/nm-settings-keyfile.html
61
+ """
62
+
63
+ config_paths: list[str] = [
64
+ "/etc/NetworkManager/system-connections/",
65
+ "/usr/lib/NetworkManager/system-connections/",
66
+ "/run/NetworkManager/system-connections/",
67
+ ]
68
+
69
+ @dataclass
70
+ class ParserContext:
71
+ source: str
72
+ uuid: str | None = None
73
+ last_connected: datetime | None = None
74
+ name: str | None = None
75
+ mac_address: str | None = None
76
+ type: str = ""
77
+ dns: set[NetAddress] = field(default_factory=set)
78
+ ip_interfaces: set[NetInterface] = field(default_factory=set)
79
+ gateways: set[NetAddress] = field(default_factory=set)
80
+ dhcp_ipv4: bool = False
81
+ dhcp_ipv6: bool = False
82
+ vlan: set[int] = field(default_factory=set)
83
+
84
+ def to_record(self) -> UnixInterfaceRecord:
85
+ return UnixInterfaceRecord(
86
+ source=self.source,
87
+ last_connected=self.last_connected,
88
+ name=self.name,
89
+ mac=[self.mac_address] if self.mac_address else [],
90
+ type=self.type,
91
+ dhcp_ipv4=self.dhcp_ipv4,
92
+ dhcp_ipv6=self.dhcp_ipv6,
93
+ dns=list(self.dns),
94
+ ip=[interface.ip for interface in self.ip_interfaces],
95
+ network=[interface.network for interface in self.ip_interfaces],
96
+ gateway=list(self.gateways),
97
+ vlan=list(self.vlan),
98
+ configurator="NetworkManager",
99
+ )
100
+
101
+ def interfaces(self) -> Iterator[UnixInterfaceRecord]:
102
+ connections: list[NetworkManagerConfigParser.ParserContext] = []
103
+ vlan_id_by_interface: VlanIdByInterface = {}
104
+
105
+ for connection_file_path in self._config_files(self.config_paths, "*"):
106
+ try:
107
+ config = configutil.parse(connection_file_path, hint="ini")
108
+ context = self.ParserContext(source=connection_file_path)
109
+ common_section: dict[str, str] = config.get("connection", {})
110
+ context.type = common_section.get("type", "")
111
+ sub_type: dict[str, str] = config.get(context.type, {})
112
+
113
+ if context.type == "vlan":
114
+ self._parse_vlan(sub_type, vlan_id_by_interface)
115
+ continue
116
+
117
+ for ip_version in ["ipv4", "ipv6"]:
118
+ ip_section: dict[str, str] = config.get(ip_version, {})
119
+ for key, value in ip_section.items():
120
+ self._parse_ip_section_key(key, value, context, ip_version)
121
+
122
+ context.name = common_section.get("interface-name")
123
+ context.mac_address = sub_type.get("mac-address")
124
+ context.uuid = common_section.get("uuid")
125
+ context.source = str(connection_file_path)
126
+ context.last_connected = self._parse_lastconnected(common_section.get("timestamp", ""))
127
+
128
+ connections.append(context)
129
+
130
+ except Exception as e:
131
+ self._target.log.warning("Error parsing network config file %s", connection_file_path)
132
+ self._target.log.debug("", exc_info=e)
133
+
134
+ for connection in connections:
135
+ vlan_ids_from_interface = vlan_id_by_interface.get(connection.name, set())
136
+ connection.vlan.update(vlan_ids_from_interface)
137
+
138
+ vlan_ids_from_uuid = vlan_id_by_interface.get(connection.uuid, set())
139
+ connection.vlan.update(vlan_ids_from_uuid)
140
+
141
+ yield connection.to_record()
142
+
143
+ def _parse_route(self, route: str) -> NetAddress | None:
144
+ """Parse a route and return gateway IP address."""
145
+ if (elements := route.split(",")) and len(elements) > 1:
146
+ return ip_address(elements[1])
147
+
148
+ return None
149
+
150
+ def _parse_lastconnected(self, last_connected: str) -> datetime | None:
151
+ """Parse last connected timestamp."""
152
+ if not last_connected:
153
+ return None
154
+
155
+ return datetime.fromtimestamp(int(last_connected), timezone.utc)
156
+
157
+ def _parse_ip_section_key(
158
+ self, key: str, value: str, context: ParserContext, ip_version: Literal["ipv4", "ipv6"]
159
+ ) -> None:
160
+ if not (trimmed := value.strip()):
161
+ return
162
+
163
+ if key == "dns":
164
+ context.dns.update(ip_address(addr) for addr in trimmed.split(";") if addr)
165
+ elif key.startswith("address"):
166
+ # Undocumented: single gateway on address line. Observed when running:
167
+ # nmcli connection add type ethernet ... ip4 192.168.2.138/24 gw4 192.168.2.1
168
+ ip, *gateway = trimmed.split(",", 1)
169
+ context.ip_interfaces.add(ip_interface(ip))
170
+ if gateway:
171
+ context.gateways.add(ip_address(gateway[0]))
172
+ elif key.startswith("gateway"):
173
+ context.gateways.add(ip_address(trimmed))
174
+ elif key == "method":
175
+ if ip_version == "ipv4":
176
+ context.dhcp_ipv4 = trimmed == "auto"
177
+ elif ip_version == "ipv6":
178
+ context.dhcp_ipv6 = trimmed == "auto"
179
+ elif key.startswith("route"):
180
+ if gateway := self._parse_route(value):
181
+ context.gateways.add(gateway)
182
+
183
+ def _parse_vlan(self, sub_type: dict[str, Any], vlan_id_by_interface: VlanIdByInterface) -> None:
184
+ parent_interface = sub_type.get("parent")
185
+ vlan_id = sub_type.get("id")
186
+ if not parent_interface or not vlan_id:
187
+ return
188
+
189
+ ids = vlan_id_by_interface.setdefault(parent_interface, set())
190
+ ids.add(int(vlan_id))
191
+
192
+
193
+ class SystemdNetworkConfigParser(LinuxNetworkConfigParser):
194
+ """Systemd network configuration parser.
195
+
196
+ Systemd network configuration files are generally in an INI-like format with some quirks.
197
+ Note that drop-in directories are not yet supported.
198
+
199
+ Documentation: https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html
200
+ """
201
+
202
+ config_paths: list[str] = [
203
+ "/etc/systemd/network/",
204
+ "/run/systemd/network/",
205
+ "/usr/lib/systemd/network/",
206
+ "/usr/local/lib/systemd/network/",
207
+ ]
208
+
209
+ class DhcpConfig(NamedTuple):
210
+ ipv4: bool
211
+ ipv6: bool
212
+
213
+ # Can be enclosed in brackets for IPv6. Can also have port, iface name, and SNI, which we ignore.
214
+ # Example: [1111:2222::3333]:9953%ifname#example.com
215
+ dns_ip_patttern = re.compile(
216
+ r"(?P<withoutBrackets>(?:\d{1,3}\.){3}\d{1,3})|\[(?P<withBrackets>\[?[0-9a-fA-F:]+\]?)\]"
217
+ )
218
+
219
+ def interfaces(self) -> Iterator:
220
+ virtual_networks = self._parse_virtual_networks()
221
+ yield from self._parse_networks(virtual_networks)
222
+
223
+ def _parse_virtual_networks(self) -> VlanIdByInterface:
224
+ """Parse virtual network configurations from systemd network configuration files."""
225
+
226
+ virtual_networks: VlanIdByInterface = {}
227
+ for config_file in self._config_files(self.config_paths, "*.netdev"):
228
+ try:
229
+ virtual_network_config = configutil.parse(config_file, hint="systemd")
230
+ net_dev_section: dict[str, str] = virtual_network_config.get("NetDev", {})
231
+ if net_dev_section.get("Kind") != "vlan":
232
+ continue
233
+
234
+ vlan_id = virtual_network_config.get("VLAN", {}).get("Id")
235
+ if (name := net_dev_section.get("Name")) and vlan_id:
236
+ vlan_ids = virtual_networks.setdefault(name, set())
237
+ vlan_ids.add(int(vlan_id))
238
+ except Exception as e:
239
+ self._target.log.warning("Error parsing virtual network config file %s", config_file)
240
+ self._target.log.debug("", exc_info=e)
241
+
242
+ return virtual_networks
243
+
244
+ def _parse_networks(self, virtual_networks: VlanIdByInterface) -> Iterator[UnixInterfaceRecord]:
245
+ """Parse network configurations from systemd network configuration files."""
246
+ for config_file in self._config_files(self.config_paths, "*.network"):
247
+ try:
248
+ config = configutil.parse(config_file, hint="systemd")
249
+
250
+ match_section: dict[str, str] = config.get("Match", {})
251
+ network_section: dict[str, str] = config.get("Network", {})
252
+ link_section: dict[str, str] = config.get("Link", {})
253
+
254
+ ip_interfaces: set[NetInterface] = set()
255
+ gateways: set[NetAddress] = set()
256
+ dns: set[NetAddress] = set()
257
+ mac_addresses: set[str] = set()
258
+
259
+ if link_mac := link_section.get("MACAddress"):
260
+ mac_addresses.add(link_mac)
261
+ mac_addresses.update(match_section.get("MACAddress", "").split())
262
+ mac_addresses.update(match_section.get("PermanentMACAddress", "").split())
263
+
264
+ dns_value = to_list(network_section.get("DNS", []))
265
+ dns.update(self._parse_dns_ip(dns_ip) for dns_ip in dns_value)
266
+
267
+ address_value = to_list(network_section.get("Address", []))
268
+ ip_interfaces.update(ip_interface(addr) for addr in address_value)
269
+
270
+ gateway_value = to_list(network_section.get("Gateway", []))
271
+ gateways.update(ip_address(gateway) for gateway in gateway_value)
272
+
273
+ vlan_ids: set[int] = set()
274
+ vlan_names = to_list(network_section.get("VLAN", []))
275
+ for vlan_name in vlan_names:
276
+ if ids := virtual_networks.get(vlan_name):
277
+ vlan_ids.update(ids)
278
+
279
+ # There are possibly multiple route sections, but they are collapsed into one by the parser.
280
+ route_section: dict[str, Any] = config.get("Route", {})
281
+ gateway_values = to_list(route_section.get("Gateway", []))
282
+ gateways.update(filter(None, map(self._parse_gateway, gateway_values)))
283
+
284
+ dhcp_ipv4, dhcp_ipv6 = self._parse_dhcp(network_section.get("DHCP"))
285
+
286
+ yield UnixInterfaceRecord(
287
+ source=str(config_file),
288
+ type=match_section.get("Type"),
289
+ enabled=None, # Unknown, dependent on run-time state
290
+ dhcp_ipv4=dhcp_ipv4,
291
+ dhcp_ipv6=dhcp_ipv6,
292
+ name=match_section.get("Name"),
293
+ dns=list(dns),
294
+ mac=list(mac_addresses),
295
+ ip=[interface.ip for interface in ip_interfaces],
296
+ network=[interface.network for interface in ip_interfaces],
297
+ gateway=list(gateways),
298
+ vlan=list(vlan_ids),
299
+ configurator="systemd-networkd",
300
+ )
301
+ except Exception as e:
302
+ self._target.log.warning("Error parsing network config file %s", config_file)
303
+ self._target.log.debug("", exc_info=e)
304
+
305
+ def _parse_dns_ip(self, address: str) -> NetAddress:
306
+ """Parse DNS address from systemd network configuration file.
307
+
308
+ The optional brackets and port number make this hard to parse.
309
+ See https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html and search for DNS.
310
+ """
311
+
312
+ if match := self.dns_ip_patttern.search(address):
313
+ return ip_address(match.group("withoutBrackets") or match.group("withBrackets"))
314
+
315
+ raise ValueError(f"Invalid DNS address format: {address}")
316
+
317
+ def _parse_dhcp(self, value: str | None) -> DhcpConfig:
318
+ """Parse DHCP value from systemd network configuration file to a named tuple (ipv4, ipv6)."""
319
+
320
+ if value is None or value == "no":
321
+ return self.DhcpConfig(ipv4=False, ipv6=False)
322
+ elif value == "yes":
323
+ return self.DhcpConfig(ipv4=True, ipv6=True)
324
+ elif value == "ipv4":
325
+ return self.DhcpConfig(ipv4=True, ipv6=False)
326
+ elif value == "ipv6":
327
+ return self.DhcpConfig(ipv4=False, ipv6=True)
328
+
329
+ raise ValueError(f"Invalid DHCP value: {value}")
330
+
331
+ def _parse_gateway(self, value: str | None) -> NetAddress | None:
332
+ if (not value) or (value in {"_dhcp4", "_ipv6ra"}):
333
+ return None
334
+
335
+ return ip_address(value)
336
+
337
+
338
+ MANAGERS = [NetworkManagerConfigParser, SystemdNetworkConfigParser]
@@ -31,15 +31,25 @@ class GnomeTrashPlugin(Plugin):
31
31
 
32
32
  def __init__(self, target: Target):
33
33
  super().__init__(target)
34
- self.trashes = list(self._garbage_collector())
34
+ self.trashes = set(self._garbage_collector())
35
35
 
36
36
  def _garbage_collector(self) -> Iterator[tuple[UserDetails, TargetPath]]:
37
37
  """it aint much, but its honest work"""
38
+
39
+ # home trash folders
38
40
  for user_details in self.target.user_details.all_with_home():
39
41
  for trash_path in self.PATHS:
40
42
  if (path := user_details.home_path.joinpath(trash_path)).exists():
41
43
  yield user_details, path
42
44
 
45
+ # mounted devices trash folders
46
+ for mount_path in list(self.target.fs.mounts) + ["/mnt", "/media"]:
47
+ if mount_path == "/":
48
+ continue
49
+
50
+ for mount_trash in self.target.fs.path(mount_path).rglob(".Trash-*"):
51
+ yield UserDetails(None, None), mount_trash
52
+
43
53
  def check_compatible(self) -> None:
44
54
  if not self.trashes:
45
55
  raise UnsupportedPluginError("No Trash folder(s) found")
@@ -52,7 +62,8 @@ class GnomeTrashPlugin(Plugin):
52
62
  Recovers deleted files and artifacts from ``$HOME/.local/share/Trash``.
53
63
  Probably also works with other desktop interfaces as long as they follow the Trash specification from FreeDesktop.
54
64
 
55
- Currently does not parse media trash locations such as ``/media/$Label/.Trash-1000/*``.
65
+ Also parses media trash locations such as ``/media/$USER/$Label/.Trash-*``, ``/mnt/$Label/.Trash-*`` and other
66
+ locations as defined in ``/etc/fstab``.
56
67
 
57
68
  Resources:
58
69
  - https://specifications.freedesktop.org/trash-spec/latest/
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import re
3
4
  from enum import IntEnum
4
5
  from functools import lru_cache
5
6
  from typing import Iterator
@@ -224,11 +225,13 @@ def _try_value(subkey: RegistryKey, value: str) -> str | list | None:
224
225
  return None
225
226
 
226
227
 
227
- def _get_config_value(key: RegistryKey, name: str) -> set:
228
+ def _get_config_value(key: RegistryKey, name: str, sep: str | None = None) -> set:
228
229
  value = _try_value(key, name)
229
230
  if not value or value in ("", "0.0.0.0", None, [], ["0.0.0.0"]):
230
231
  return set()
231
-
232
+ if sep and isinstance(value, str):
233
+ re_sep = "|".join(map(re.escape, sep))
234
+ value = re.split(re_sep, value)
232
235
  if isinstance(value, list):
233
236
  return set(value)
234
237
 
@@ -281,7 +284,8 @@ class WindowsNetworkPlugin(NetworkPlugin):
281
284
  pass
282
285
 
283
286
  # Extract the rest of the device information
284
- device_info["mac"] = _try_value(subkey, "NetworkAddress")
287
+ if mac_address := _try_value(subkey, "NetworkAddress"):
288
+ device_info["mac"] = [mac_address]
285
289
  device_info["vlan"] = _try_value(subkey, "VlanID")
286
290
 
287
291
  if timestamp := _try_value(subkey, "NetworkInterfaceInstallTimestamp"):
@@ -354,11 +358,11 @@ class WindowsNetworkPlugin(NetworkPlugin):
354
358
  dhcp_config["ip"].update(_get_config_value(key, "DhcpIPAddress"))
355
359
  dhcp_config["subnetmask"].update(_get_config_value(key, "DhcpSubnetMask"))
356
360
  dhcp_config["search_domain"].update(_get_config_value(key, "DhcpDomain"))
357
- dhcp_config["dns"].update(_get_config_value(key, "DhcpNameServer"))
361
+ dhcp_config["dns"].update(_get_config_value(key, "DhcpNameServer", " ,"))
358
362
 
359
363
  # Extract static configuration from the registry
360
364
  static_config["gateway"].update(_get_config_value(key, "DefaultGateway"))
361
- static_config["dns"].update(_get_config_value(key, "NameServer"))
365
+ static_config["dns"].update(_get_config_value(key, "NameServer", " ,"))
362
366
  static_config["search_domain"].update(_get_config_value(key, "Domain"))
363
367
  static_config["ip"].update(_get_config_value(key, "IPAddress"))
364
368
  static_config["subnetmask"].update(_get_config_value(key, "SubnetMask"))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.20.dev64
3
+ Version: 3.20.1
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
@@ -23,81 +23,81 @@ Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  License-File: COPYRIGHT
25
25
  Requires-Dist: defusedxml
26
- Requires-Dist: dissect.cstruct <5,>=4
27
- Requires-Dist: dissect.eventlog <4,>=3
28
- Requires-Dist: dissect.evidence <4,>=3
29
- Requires-Dist: dissect.hypervisor <4,>=3
30
- Requires-Dist: dissect.ntfs <4,>=3.4
31
- Requires-Dist: dissect.regf <4,>=3.3
32
- Requires-Dist: dissect.util <4,>=3
33
- Requires-Dist: dissect.volume <4,>=2
34
- Requires-Dist: flow.record ~=3.18.0
26
+ Requires-Dist: dissect.cstruct<5,>=4
27
+ Requires-Dist: dissect.eventlog<4,>=3
28
+ Requires-Dist: dissect.evidence<4,>=3
29
+ Requires-Dist: dissect.hypervisor<4,>=3
30
+ Requires-Dist: dissect.ntfs<4,>=3.4
31
+ Requires-Dist: dissect.regf<4,>=3.3
32
+ Requires-Dist: dissect.util<4,>=3
33
+ Requires-Dist: dissect.volume<4,>=2
34
+ Requires-Dist: flow.record~=3.18.0
35
35
  Requires-Dist: structlog
36
- Provides-Extra: cb
37
- Requires-Dist: dissect.target[full] ; extra == 'cb'
38
- Requires-Dist: carbon-black-cloud-sdk ~=1.4.3 ; extra == 'cb'
39
- Provides-Extra: dev
40
- Requires-Dist: dissect.target[full,mqtt,yara] ; extra == 'dev'
41
- Requires-Dist: dissect.archive[dev] <2.0.dev,>=1.0.dev ; extra == 'dev'
42
- Requires-Dist: dissect.btrfs[dev] <2.0.dev,>=1.0.dev ; extra == 'dev'
43
- Requires-Dist: dissect.cim[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
44
- Requires-Dist: dissect.clfs[dev] <2.0.dev,>=1.0.dev ; extra == 'dev'
45
- Requires-Dist: dissect.cstruct <5.0.dev,>=4.0.dev ; extra == 'dev'
46
- Requires-Dist: dissect.esedb[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
47
- Requires-Dist: dissect.etl[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
48
- Requires-Dist: dissect.eventlog[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
49
- Requires-Dist: dissect.evidence[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
50
- Requires-Dist: dissect.extfs[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
51
- Requires-Dist: dissect.fat[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
52
- Requires-Dist: dissect.ffs[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
53
- Requires-Dist: dissect.hypervisor[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
54
- Requires-Dist: dissect.jffs[dev] <2.0.dev,>=1.0.dev ; extra == 'dev'
55
- Requires-Dist: dissect.ntfs[dev] <4.0.dev,>=3.4.dev ; extra == 'dev'
56
- Requires-Dist: dissect.regf[dev] <4.0.dev,>=3.3.dev ; extra == 'dev'
57
- Requires-Dist: dissect.shellitem[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
58
- Requires-Dist: dissect.sql[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
59
- Requires-Dist: dissect.squashfs[dev] <2.0.dev,>=1.0.dev ; extra == 'dev'
60
- Requires-Dist: dissect.thumbcache[dev] <2.0.dev,>=1.0.dev ; extra == 'dev'
61
- Requires-Dist: dissect.util <4.0.dev,>=3.0.dev ; extra == 'dev'
62
- Requires-Dist: dissect.vmfs[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
63
- Requires-Dist: dissect.volume[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
64
- Requires-Dist: dissect.xfs[dev] <4.0.dev,>=3.0.dev ; extra == 'dev'
65
- Requires-Dist: dissect.fve[dev] <5.0.dev,>=4.0.dev ; (platform_system != "Windows" or platform_python_implementation != "PyPy") and extra == 'dev'
66
36
  Provides-Extra: full
67
- Requires-Dist: asn1crypto ; extra == 'full'
68
- Requires-Dist: dissect.archive <2,>=1 ; extra == 'full'
69
- Requires-Dist: dissect.btrfs <2,>=1 ; extra == 'full'
70
- Requires-Dist: dissect.cim <4,>=3 ; extra == 'full'
71
- Requires-Dist: dissect.clfs <2,>=1 ; extra == 'full'
72
- Requires-Dist: dissect.esedb <4,>=3 ; extra == 'full'
73
- Requires-Dist: dissect.etl <4,>=3 ; extra == 'full'
74
- Requires-Dist: dissect.extfs <4,>=3 ; extra == 'full'
75
- Requires-Dist: dissect.fat <4,>=3 ; extra == 'full'
76
- Requires-Dist: dissect.ffs <4,>=3 ; extra == 'full'
77
- Requires-Dist: dissect.jffs <2,>=1 ; extra == 'full'
78
- Requires-Dist: dissect.ole <4,>=3 ; extra == 'full'
79
- Requires-Dist: dissect.shellitem <4,>=3 ; extra == 'full'
80
- Requires-Dist: dissect.squashfs <2,>=1 ; extra == 'full'
81
- Requires-Dist: dissect.sql <4,>=3 ; extra == 'full'
82
- Requires-Dist: dissect.thumbcache <2,>=1 ; extra == 'full'
83
- Requires-Dist: dissect.vmfs <4,>=3 ; extra == 'full'
84
- Requires-Dist: dissect.xfs <4,>=3 ; extra == 'full'
85
- Requires-Dist: ipython ; extra == 'full'
86
- Requires-Dist: fusepy ; extra == 'full'
87
- Requires-Dist: pycryptodome ; extra == 'full'
88
- Requires-Dist: ruamel.yaml ; extra == 'full'
89
- Requires-Dist: zstandard ; extra == 'full'
90
- Requires-Dist: dissect.fve <5,>=4 ; (platform_system != "Windows" or platform_python_implementation != "PyPy") and extra == 'full'
91
- Requires-Dist: tomli ; (python_version < "3.11") and extra == 'full'
92
- Provides-Extra: mqtt
93
- Requires-Dist: dissect.target[full] ; extra == 'mqtt'
94
- Requires-Dist: paho-mqtt ==1.6.1 ; extra == 'mqtt'
95
- Provides-Extra: smb
96
- Requires-Dist: dissect.target[full] ; extra == 'smb'
97
- Requires-Dist: impacket ==0.10.0 ; extra == 'smb'
37
+ Requires-Dist: asn1crypto; extra == "full"
38
+ Requires-Dist: dissect.archive<2,>=1; extra == "full"
39
+ Requires-Dist: dissect.btrfs<2,>=1; extra == "full"
40
+ Requires-Dist: dissect.cim<4,>=3; extra == "full"
41
+ Requires-Dist: dissect.clfs<2,>=1; extra == "full"
42
+ Requires-Dist: dissect.esedb<4,>=3; extra == "full"
43
+ Requires-Dist: dissect.etl<4,>=3; extra == "full"
44
+ Requires-Dist: dissect.extfs<4,>=3; extra == "full"
45
+ Requires-Dist: dissect.fat<4,>=3; extra == "full"
46
+ Requires-Dist: dissect.ffs<4,>=3; extra == "full"
47
+ Requires-Dist: dissect.fve<5,>=4; (platform_system != "Windows" or platform_python_implementation != "PyPy") and extra == "full"
48
+ Requires-Dist: dissect.jffs<2,>=1; extra == "full"
49
+ Requires-Dist: dissect.ole<4,>=3; extra == "full"
50
+ Requires-Dist: dissect.shellitem<4,>=3; extra == "full"
51
+ Requires-Dist: dissect.squashfs<2,>=1; extra == "full"
52
+ Requires-Dist: dissect.sql<4,>=3; extra == "full"
53
+ Requires-Dist: dissect.thumbcache<2,>=1; extra == "full"
54
+ Requires-Dist: dissect.vmfs<4,>=3; extra == "full"
55
+ Requires-Dist: dissect.xfs<4,>=3; extra == "full"
56
+ Requires-Dist: ipython; extra == "full"
57
+ Requires-Dist: fusepy; extra == "full"
58
+ Requires-Dist: pycryptodome; extra == "full"
59
+ Requires-Dist: ruamel.yaml; extra == "full"
60
+ Requires-Dist: tomli; python_version < "3.11" and extra == "full"
61
+ Requires-Dist: zstandard; extra == "full"
62
+ Provides-Extra: dev
63
+ Requires-Dist: dissect.target[full,mqtt,yara]; extra == "dev"
64
+ Requires-Dist: dissect.archive[dev]<2.0.dev,>=1.0.dev; extra == "dev"
65
+ Requires-Dist: dissect.btrfs[dev]<2.0.dev,>=1.0.dev; extra == "dev"
66
+ Requires-Dist: dissect.cim[dev]<4.0.dev,>=3.0.dev; extra == "dev"
67
+ Requires-Dist: dissect.clfs[dev]<2.0.dev,>=1.0.dev; extra == "dev"
68
+ Requires-Dist: dissect.cstruct<5.0.dev,>=4.0.dev; extra == "dev"
69
+ Requires-Dist: dissect.esedb[dev]<4.0.dev,>=3.0.dev; extra == "dev"
70
+ Requires-Dist: dissect.etl[dev]<4.0.dev,>=3.0.dev; extra == "dev"
71
+ Requires-Dist: dissect.eventlog[dev]<4.0.dev,>=3.0.dev; extra == "dev"
72
+ Requires-Dist: dissect.evidence[dev]<4.0.dev,>=3.0.dev; extra == "dev"
73
+ Requires-Dist: dissect.extfs[dev]<4.0.dev,>=3.0.dev; extra == "dev"
74
+ Requires-Dist: dissect.fat[dev]<4.0.dev,>=3.0.dev; extra == "dev"
75
+ Requires-Dist: dissect.ffs[dev]<4.0.dev,>=3.0.dev; extra == "dev"
76
+ Requires-Dist: dissect.fve[dev]<5.0.dev,>=4.0.dev; (platform_system != "Windows" or platform_python_implementation != "PyPy") and extra == "dev"
77
+ Requires-Dist: dissect.hypervisor[dev]<4.0.dev,>=3.0.dev; extra == "dev"
78
+ Requires-Dist: dissect.jffs[dev]<2.0.dev,>=1.0.dev; extra == "dev"
79
+ Requires-Dist: dissect.ntfs[dev]<4.0.dev,>=3.4.dev; extra == "dev"
80
+ Requires-Dist: dissect.regf[dev]<4.0.dev,>=3.3.dev; extra == "dev"
81
+ Requires-Dist: dissect.shellitem[dev]<4.0.dev,>=3.0.dev; extra == "dev"
82
+ Requires-Dist: dissect.sql[dev]<4.0.dev,>=3.0.dev; extra == "dev"
83
+ Requires-Dist: dissect.squashfs[dev]<2.0.dev,>=1.0.dev; extra == "dev"
84
+ Requires-Dist: dissect.thumbcache[dev]<2.0.dev,>=1.0.dev; extra == "dev"
85
+ Requires-Dist: dissect.util<4.0.dev,>=3.0.dev; extra == "dev"
86
+ Requires-Dist: dissect.vmfs[dev]<4.0.dev,>=3.0.dev; extra == "dev"
87
+ Requires-Dist: dissect.volume[dev]<4.0.dev,>=3.0.dev; extra == "dev"
88
+ Requires-Dist: dissect.xfs[dev]<4.0.dev,>=3.0.dev; extra == "dev"
98
89
  Provides-Extra: yara
99
- Requires-Dist: dissect.target[full] ; extra == 'yara'
100
- Requires-Dist: yara-python ; extra == 'yara'
90
+ Requires-Dist: dissect.target[full]; extra == "yara"
91
+ Requires-Dist: yara-python; extra == "yara"
92
+ Provides-Extra: smb
93
+ Requires-Dist: dissect.target[full]; extra == "smb"
94
+ Requires-Dist: impacket==0.10.0; extra == "smb"
95
+ Provides-Extra: cb
96
+ Requires-Dist: dissect.target[full]; extra == "cb"
97
+ Requires-Dist: carbon-black-cloud-sdk~=1.4.3; extra == "cb"
98
+ Provides-Extra: mqtt
99
+ Requires-Dist: dissect.target[full]; extra == "mqtt"
100
+ Requires-Dist: paho-mqtt==1.6.1; extra == "mqtt"
101
101
 
102
102
  # dissect.target
103
103