dissect.target 3.20.dev64__py3-none-any.whl → 3.20.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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