dissect.target 3.21.dev1__py3-none-any.whl → 3.21.dev3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -891,7 +891,7 @@ KNOWN_FILES: dict[str, type[ConfigurationParser]] = {
891
891
  }
892
892
 
893
893
 
894
- def parse(path: Union[FilesystemEntry, TargetPath], hint: Optional[str] = None, *args, **kwargs) -> ConfigParser:
894
+ def parse(path: Union[FilesystemEntry, TargetPath], hint: Optional[str] = None, *args, **kwargs) -> ConfigurationParser:
895
895
  """Parses the content of an ``path`` or ``entry`` to a dictionary.
896
896
 
897
897
  Args:
@@ -922,7 +922,7 @@ def parse_config(
922
922
  entry: FilesystemEntry,
923
923
  hint: Optional[str] = None,
924
924
  options: Optional[ParserOptions] = None,
925
- ) -> ConfigParser:
925
+ ) -> ConfigurationParser:
926
926
  parser_type = _select_parser(entry, hint)
927
927
 
928
928
  parser = parser_type.create_parser(options)
@@ -145,33 +145,40 @@ EmptyRecord = RecordDescriptor(
145
145
 
146
146
  COMMON_INTERFACE_ELEMENTS = [
147
147
  ("string", "name"),
148
+ ("string[]", "mac"),
148
149
  ("string", "type"),
149
150
  ("boolean", "enabled"),
150
- ("string", "mac"),
151
151
  ("net.ipaddress[]", "dns"),
152
152
  ("net.ipaddress[]", "ip"),
153
153
  ("net.ipaddress[]", "gateway"),
154
+ ("net.ipnetwork[]", "network"),
154
155
  ("string", "source"),
155
156
  ]
156
157
 
157
158
 
158
159
  UnixInterfaceRecord = TargetRecordDescriptor(
159
160
  "unix/network/interface",
160
- COMMON_INTERFACE_ELEMENTS,
161
+ [
162
+ *COMMON_INTERFACE_ELEMENTS,
163
+ ("boolean", "dhcp_ipv4"), # NetworkManager allows for dual-stack configurations.
164
+ ("boolean", "dhcp_ipv6"),
165
+ ("datetime", "last_connected"),
166
+ ("varint[]", "vlan"),
167
+ ("string", "configurator"),
168
+ ],
161
169
  )
162
170
 
163
171
  WindowsInterfaceRecord = TargetRecordDescriptor(
164
172
  "windows/network/interface",
165
173
  [
166
174
  *COMMON_INTERFACE_ELEMENTS,
167
- ("varint", "vlan"),
168
- ("net.ipnetwork[]", "network"),
169
175
  ("varint", "metric"),
170
176
  ("stringlist", "search_domain"),
171
177
  ("datetime", "first_connected"),
172
178
  ("datetime", "last_connected"),
173
179
  ("net.ipaddress[]", "subnetmask"),
174
180
  ("boolean", "dhcp"),
181
+ ("varint", "vlan"),
175
182
  ],
176
183
  )
177
184
 
@@ -179,10 +186,9 @@ MacInterfaceRecord = TargetRecordDescriptor(
179
186
  "macos/network/interface",
180
187
  [
181
188
  *COMMON_INTERFACE_ELEMENTS,
182
- ("varint", "vlan"),
183
- ("net.ipnetwork[]", "network"),
184
189
  ("varint", "interface_service_order"),
185
190
  ("boolean", "dhcp"),
191
+ ("varint", "vlan"),
186
192
  ],
187
193
  )
188
194
 
@@ -1,10 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import re
3
5
  import urllib.parse
4
6
  from datetime import datetime, timezone, tzinfo
5
7
  from enum import Enum
6
8
  from pathlib import Path
7
- from typing import BinaryIO, Callable, Iterator, Optional, Union
9
+ from typing import BinaryIO, Callable, Iterator, Optional, TypeVar, Union
8
10
 
9
11
  from dissect.util.ts import from_unix
10
12
 
@@ -24,6 +26,23 @@ def findall(buf: bytes, needle: bytes) -> Iterator[int]:
24
26
  offset += 1
25
27
 
26
28
 
29
+ T = TypeVar("T")
30
+
31
+
32
+ def to_list(value: T | list[T]) -> list[T]:
33
+ """Convert a single value or a list of values to a list.
34
+
35
+ Args:
36
+ value: The value to convert.
37
+
38
+ Returns:
39
+ A list of values.
40
+ """
41
+ if not isinstance(value, list):
42
+ return [value]
43
+ return value
44
+
45
+
27
46
  class StrEnum(str, Enum):
28
47
  """Sortable and serializible string-based enum"""
29
48
 
@@ -79,7 +79,7 @@ class NetworkPlugin(Plugin):
79
79
  @internal
80
80
  def with_mac(self, mac: str) -> Iterator[InterfaceRecord]:
81
81
  for interface in self.interfaces():
82
- if interface.mac == mac:
82
+ if mac in interface.mac:
83
83
  yield interface
84
84
 
85
85
  @internal
@@ -84,9 +84,10 @@ class MacNetworkPlugin(NetworkPlugin):
84
84
  network=network,
85
85
  interface_service_order=interface_service_order,
86
86
  dhcp=dhcp,
87
+ mac=[],
87
88
  _target=self.target,
88
89
  )
89
90
 
90
91
  except Exception as e:
91
- self.target.log.warning("Error reading configuration for network device %s: %s", name, e)
92
+ self.target.log.warning("Error reading configuration for network device %s", name, exc_info=e)
92
93
  continue
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import fnmatch
2
4
  import re
3
5
  from pathlib import Path
@@ -30,9 +32,10 @@ class EtcTree(ConfigurationTreePlugin):
30
32
  def __init__(self, target: Target):
31
33
  super().__init__(target, "/etc")
32
34
 
33
- def _sub(self, items: ConfigurationEntry, entry: Path, pattern: str) -> Iterator[UnixConfigTreeRecord]:
35
+ def _sub(
36
+ self, items: ConfigurationEntry | dict, entry: Path, orig_path: Path, pattern: str
37
+ ) -> Iterator[UnixConfigTreeRecord]:
34
38
  index = 0
35
- config_entry = items
36
39
  if not isinstance(items, dict):
37
40
  items = items.as_dict()
38
41
 
@@ -41,7 +44,7 @@ class EtcTree(ConfigurationTreePlugin):
41
44
  path = Path(entry) / Path(key)
42
45
 
43
46
  if isinstance(value, dict):
44
- yield from self._sub(value, path, pattern)
47
+ yield from self._sub(value, path, orig_path, pattern)
45
48
  continue
46
49
 
47
50
  if not isinstance(value, list):
@@ -50,7 +53,7 @@ class EtcTree(ConfigurationTreePlugin):
50
53
  if fnmatch.fnmatch(path, pattern):
51
54
  data = {
52
55
  "_target": self.target,
53
- "source": self.target.fs.path(config_entry.entry.path),
56
+ "source": self.target.fs.path(orig_path),
54
57
  "path": path,
55
58
  "key": key,
56
59
  "value": value,
@@ -71,8 +74,11 @@ class EtcTree(ConfigurationTreePlugin):
71
74
  for entry, subs, items in self.config_fs.walk(root):
72
75
  for item in items:
73
76
  try:
74
- config_object = self.get(str(Path(entry) / Path(item)))
75
- yield from self._sub(config_object, Path(entry) / Path(item), pattern)
77
+ path = Path(entry) / item
78
+ config_object = self.get(str(path))
79
+
80
+ if isinstance(config_object, ConfigurationEntry):
81
+ yield from self._sub(config_object, path, orig_path=path, pattern=pattern)
76
82
  except Exception:
77
83
  self.target.log.warning("Could not open configuration item: %s", item)
78
84
  pass
@@ -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]
@@ -281,7 +281,8 @@ class WindowsNetworkPlugin(NetworkPlugin):
281
281
  pass
282
282
 
283
283
  # Extract the rest of the device information
284
- device_info["mac"] = _try_value(subkey, "NetworkAddress")
284
+ if mac_address := _try_value(subkey, "NetworkAddress"):
285
+ device_info["mac"] = [mac_address]
285
286
  device_info["vlan"] = _try_value(subkey, "VlanID")
286
287
 
287
288
  if timestamp := _try_value(subkey, "NetworkInterfaceInstallTimestamp"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.21.dev1
3
+ Version: 3.21.dev3
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
 
@@ -46,7 +46,7 @@ dissect/target/filesystems/zip.py,sha256=BeNj23DOYfWuTm5V1V419ViJiMfBrO1VA5gP6rl
46
46
  dissect/target/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  dissect/target/helpers/cache.py,sha256=TXlJBdFRz6V9zKs903am4Yawr0maYw5kZY0RqklDQJM,8568
48
48
  dissect/target/helpers/config.py,sha256=RMHnIuKJHINHiLrvKN3EyA0jFA1o6-pbeaycG8Pgrp8,2596
49
- dissect/target/helpers/configutil.py,sha256=KuuQoJZMr69247-yDKylLUSfSsoE4sGXy3a145CwDa8,31414
49
+ dissect/target/helpers/configutil.py,sha256=xXCmQA939iacd6cHr1SwNv6dyUo2bKMWsAI0TxwL24I,31428
50
50
  dissect/target/helpers/cyber.py,sha256=2kAA2YjWnPfKn_aTmRSse4uB64lq6AnNX19TD8Alylc,16830
51
51
  dissect/target/helpers/descriptor_extensions.py,sha256=uT8GwznfDAiIgMM7JKKOY0PXKMv2c0GCqJTCkWFgops,2605
52
52
  dissect/target/helpers/docs.py,sha256=G5Ll1cX2kYUfNcFFI-qjeUQ5Di60RAVpVnlB72F4Vqk,5153
@@ -60,12 +60,12 @@ dissect/target/helpers/mount.py,sha256=7Y4B0uY2c00p23hmuMKSgwAwxkKcY4spyAMO1kdcV
60
60
  dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q,22258
61
61
  dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
62
62
  dissect/target/helpers/protobuf.py,sha256=b4DsnqrRLrefcDjx7rQno-_LBcwtJXxuKf5RdOegzfE,1537
63
- dissect/target/helpers/record.py,sha256=TxG1lwW5N0V-7kuq4s2vnQh-hAbT7rVwwZLf4sIDNjM,6334
63
+ dissect/target/helpers/record.py,sha256=VRwPE8OIotWzfxw-_ep_eXG-Iml6xzhodwPlbQYYaoY,6540
64
64
  dissect/target/helpers/record_modifier.py,sha256=cRNDhUYMmx4iEKyEr5Pqy9xiFgxr_GBNJPp_omkQsEU,4094
65
65
  dissect/target/helpers/regutil.py,sha256=hnJRTOQs4IqN4EroBoSNC4HDvuA8w6U1dqNBvu-ILn0,28376
66
66
  dissect/target/helpers/shell_application_ids.py,sha256=hYxrP-YtHK7ZM0ectJFHfoMB8QUXLbYNKmKXMWLZRlA,38132
67
67
  dissect/target/helpers/shell_folder_ids.py,sha256=Behhb8oh0kMxrEk6YYKYigCDZe8Hw5QS6iK_d2hTs2Y,24978
68
- dissect/target/helpers/utils.py,sha256=K3xVq9D0FwIhTBAuiWN8ph7Pq2GABgG3hOz-3AmKuEA,4244
68
+ dissect/target/helpers/utils.py,sha256=1UZNTUVBmtS2clbyIsi6mHOVw0jqPktM8OwCxCbOiY0,4587
69
69
  dissect/target/helpers/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
70
  dissect/target/helpers/compat/path_310.py,sha256=PsLDIodlp3Hv5u-w7GDl6_LnTtchBYcRjz2MicX1egg,16982
71
71
  dissect/target/helpers/compat/path_311.py,sha256=2aydxCMWu1pN8PTBCo8HUbHRMC1xO-hj013j4QxaogE,18182
@@ -190,7 +190,7 @@ dissect/target/plugins/general/config.py,sha256=Mdy9uhWn4OJ96zfXpLgjVifV5SrViqHn
190
190
  dissect/target/plugins/general/default.py,sha256=8W_9JV3jKEeETlyTrB25sACoIIFmmO8wlVU5Zoi51W0,1425
191
191
  dissect/target/plugins/general/example.py,sha256=mYAbhtfQmUBj2L2C1DFt9bWpI7rQLJwCIYUsNLcA_pc,6053
192
192
  dissect/target/plugins/general/loaders.py,sha256=z_t55Q1XNjmTOxq0E4tCwpZ-utFyxiLKyAJIFgJMlJs,1508
193
- dissect/target/plugins/general/network.py,sha256=TWfSdI5fTgwe1_nV7u_ldtvvRwgmkVFLd4XFzy4cEZU,3257
193
+ dissect/target/plugins/general/network.py,sha256=I9wdFbBkDik1S9zvTi7sN20MdjJ2c_5tT8X8bgxWx5U,3257
194
194
  dissect/target/plugins/general/osinfo.py,sha256=oU-vmMiA-oaSEQWTSyn6-yQiH2sLQT6aTQHRd0677wo,1415
195
195
  dissect/target/plugins/general/plugins.py,sha256=9KJ70YvYwBfxt19C9yISv8YE4mOdHNvP16fTCTHC68U,6033
196
196
  dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6vWVtER9vjTg,6651
@@ -220,12 +220,12 @@ dissect/target/plugins/os/unix/bsd/openbsd/__init__.py,sha256=47DEQpj8HBSa-_TImW
220
220
  dissect/target/plugins/os/unix/bsd/openbsd/_os.py,sha256=9npz-osM-wHmjOACUqof5N5HJeps7J8KuyenUS5MZDs,923
221
221
  dissect/target/plugins/os/unix/bsd/osx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
222
222
  dissect/target/plugins/os/unix/bsd/osx/_os.py,sha256=hNFB1rwahLwgZD1kc3T4xalFusT88EoxM2Mh-8jOW_w,3440
223
- dissect/target/plugins/os/unix/bsd/osx/network.py,sha256=0Qf1jsCDNPmc_L-AmrvHjXaN_x-AtT1Ow3tdQOvFRsk,3734
223
+ dissect/target/plugins/os/unix/bsd/osx/network.py,sha256=3m71T-T-DnZFVidrokuHpr89lZOe_4drt-Xwam1ebfw,3767
224
224
  dissect/target/plugins/os/unix/bsd/osx/user.py,sha256=5rsGhsntBW9IXYIOrLpfYpSsJcBDL61QJkuZ456lXlE,2411
225
225
  dissect/target/plugins/os/unix/esxi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
226
226
  dissect/target/plugins/os/unix/esxi/_os.py,sha256=s6pAgUyfHh3QcY6sgvk5uVMmLvqK1tIHWR7MSbrFn8w,17789
227
227
  dissect/target/plugins/os/unix/etc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
228
- dissect/target/plugins/os/unix/etc/etc.py,sha256=QngR0nA1azvbNTau4U9-jKOjSoGdyduDpyEna_6yxWY,2636
228
+ dissect/target/plugins/os/unix/etc/etc.py,sha256=YSCRZZfQvmzaR5VWhTJhB8pIGliL6Nw5ruhdfvYKYaM,2783
229
229
  dissect/target/plugins/os/unix/linux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
230
230
  dissect/target/plugins/os/unix/linux/_os.py,sha256=k1aHhWqocSHMVbF54VDw9wqwa0QSToOa69TMKAyQcxw,2979
231
231
  dissect/target/plugins/os/unix/linux/cmdline.py,sha256=n_Uetoplx33XpIY27oPtMaw1E2AbAEeGLCSkxHshWgY,1673
@@ -233,6 +233,7 @@ dissect/target/plugins/os/unix/linux/environ.py,sha256=n7KttVzUtBHTIXQuS1DI5Azv6
233
233
  dissect/target/plugins/os/unix/linux/iptables.py,sha256=qTzY5PHHXA33WnPYb5NESgoSwI7ECZ8YPoEe_Fmln-8,6045
234
234
  dissect/target/plugins/os/unix/linux/modules.py,sha256=-LThb5mcKtngVfIICpdOGLtgJPc99WQ8Qufwddt8YgQ,2500
235
235
  dissect/target/plugins/os/unix/linux/netstat.py,sha256=EBpbK4BD3pZ0fKCR3ZMmVip4eQ0f6x_9yumA8vsUKPw,1691
236
+ dissect/target/plugins/os/unix/linux/network.py,sha256=KfGfYhtrzpHHefaHjTpdbGSLu6IN4anweYt3V02D9zU,14392
236
237
  dissect/target/plugins/os/unix/linux/network_managers.py,sha256=nr0gA3hBqsvP9k6xAYmRYwGRB1kMtXB1k0pe78jWdFI,25768
237
238
  dissect/target/plugins/os/unix/linux/proc.py,sha256=jm35fAasnNbObN2tpflwQuCfVYLDkTP2EDrzYG42ZSk,23354
238
239
  dissect/target/plugins/os/unix/linux/processes.py,sha256=xAJswf06HZsY8JhQ11xfJw1OLTZ1q9XZbu7_a7k2UpY,2019
@@ -289,7 +290,7 @@ dissect/target/plugins/os/windows/generic.py,sha256=RJ1znzsIa4CFxmdMh91SjMY_pnjw
289
290
  dissect/target/plugins/os/windows/jumplist.py,sha256=3gZk6O1B3lKK2Jxe0B-HapOCEehk94CYNvCVDpQC9nQ,11773
290
291
  dissect/target/plugins/os/windows/lnk.py,sha256=KTqhw0JMW-KjAxe4xlRDNSRSx-th-_nPVgTGyBaKmW0,7891
291
292
  dissect/target/plugins/os/windows/locale.py,sha256=QiLWGgWrGBGHiXgep5iSOo6VNim4YC-xd4MdW0BUJPA,2486
292
- dissect/target/plugins/os/windows/network.py,sha256=ni-qK1PyA3UJD3lRJZGEBLAXcwDVKXPa3rIor9G5OSw,11283
293
+ dissect/target/plugins/os/windows/network.py,sha256=lziv_oqmRaVKLW-Ep8CZqfd160eVmL1RswTlPgoVapg,11336
293
294
  dissect/target/plugins/os/windows/notifications.py,sha256=xxfMEY_noDxMVqvT3QS1a3j-X3qAYikOtT6v2owxuCY,17480
294
295
  dissect/target/plugins/os/windows/prefetch.py,sha256=wbbYoy05gWbJfRsM2ci4wPG7kM58OocVwXD3hkQlbRw,10647
295
296
  dissect/target/plugins/os/windows/recyclebin.py,sha256=zx58hDCvcrD_eJl9nJmr_i80krSN03ya8nQzWFr2Tw0,4917
@@ -380,10 +381,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
380
381
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
381
382
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
382
383
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
383
- dissect.target-3.21.dev1.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
384
- dissect.target-3.21.dev1.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
385
- dissect.target-3.21.dev1.dist-info/METADATA,sha256=U1ZlYrqE3MK3Bz0cfFnkwamJFHYozo9JiH2cXiAlt2c,13302
386
- dissect.target-3.21.dev1.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
387
- dissect.target-3.21.dev1.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
388
- dissect.target-3.21.dev1.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
389
- dissect.target-3.21.dev1.dist-info/RECORD,,
384
+ dissect.target-3.21.dev3.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
385
+ dissect.target-3.21.dev3.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
386
+ dissect.target-3.21.dev3.dist-info/METADATA,sha256=krLCWFNjd5yyUqcYsAEFm-PSOsLJLMLhTOyG54hXbvg,13186
387
+ dissect.target-3.21.dev3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
388
+ dissect.target-3.21.dev3.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
389
+ dissect.target-3.21.dev3.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
390
+ dissect.target-3.21.dev3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5